diff --git a/.eslintrc.json b/.eslintrc.json index bb8cf16..e980526 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -10,10 +10,16 @@ ], "rules": { "@typescript-eslint/no-unused-vars": [ - "warn" + "error", + { + "argsIgnorePattern": "^_" + } ], "no-unused-vars": [ - "warn" + "error", + { + "argsIgnorePattern": "^_" + } ], "@typescript-eslint/no-explicit-any": "warn", "prettier/prettier": "error", diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index 3c31b8a..57ce264 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -51,7 +51,7 @@ jobs: - name: Extract repo name id: repo - run: echo "REPO_NAME=$(echo ${{ github.repository }} | awk -F'/' '{print $2}')" >> $GITHUB_ENV + run: echo "REPO_NAME=$(echo ${{ github.repository }} | awk -F'/' '{print tolower($2)}')" >> $GITHUB_ENV - name: Build and push Docker image uses: docker/build-push-action@v5 @@ -62,4 +62,4 @@ jobs: - name: Image digest run: | - echo "Image pushed: ${{ secrets.DOCKERHUB_USERNAME }}/${{ env.REPO_NAME }}:${{ github.sha }}" \ No newline at end of file + echo "Image pushed: ${{ secrets.DOCKERHUB_USERNAME }}/${{ env.REPO_NAME }}:${{ github.sha }}" diff --git a/.gitignore b/.gitignore index 36983e4..e90b4a1 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,6 @@ .env .env.local env.local -docker-compose.yml # Dependencies node_modules/ diff --git a/TEST_PLAN.md b/TEST_PLAN.md new file mode 100644 index 0000000..365b7a1 --- /dev/null +++ b/TEST_PLAN.md @@ -0,0 +1,140 @@ +# Test Coverage Improvement Plan + +## Current Coverage Status +- Overall: 27.05% +- Branches: 53.65% +- Functions: 11.47% +- Lines: 27.05% + +## Priority Areas + +### 1. Controllers (7.8%) + +#### High Priority +- `auth.controller.ts` (18.21%) + - [ ] Test successful user registration + - [ ] Test password validation + - [ ] Test email validation + - [ ] Test OTP verification + - [ ] Test OAuth flows (Google/Facebook) + - [ ] Test refresh token functionality + +#### Medium Priority +- `product.controller.ts` (7.89%) + - [ ] Test product creation + - [ ] Test product updates + - [ ] Test product deletion + - [ ] Test product listing + - [ ] Test product search + +- `order.controller.ts` (5.58%) + - [ ] Test order creation + - [ ] Test order status updates + - [ ] Test order listing + - [ ] Test order cancellation + +#### Lower Priority +- `notification.controller.ts` (5.18%) +- `product.search.controller.ts` (4.95%) +- `review.controller.ts` (4.77%) +- `user.controller.ts` (7.1%) + +### 2. Middleware (26.07%) + +#### High Priority +- `auth-check.ts` (6.77%) + - [ ] Test token validation + - [ ] Test role-based access + - [ ] Test expired tokens + - [ ] Test malformed tokens + +#### Medium Priority +- `multerStorage.ts` (33.92%) + - [ ] Test file upload success + - [ ] Test file size limits + - [ ] Test file type validation + - [ ] Test storage configuration + +- `handleExpoResponse.ts` (10.86%) + - [ ] Test notification sending + - [ ] Test error handling + - [ ] Test device token validation + +#### Lower Priority +- `send-notification.ts` (7.4%) +- `rateLimiter.ts` (76.19% - already decent) + +### 3. Models (73.21%) + +#### High Priority +- `index.ts` (0%) + - [ ] Test model associations + - [ ] Test database connection + - [ ] Test model synchronization + +### 4. Routes (72.39%) + +#### High Priority +- `payment.collection.routes.ts` (64.51%) + - [ ] Test payment initiation + - [ ] Test payment callbacks + - [ ] Test payment status updates + +#### Medium Priority +- `review.routes.ts` (60%) + - [ ] Test review creation + - [ ] Test review updates + - [ ] Test review deletion + +- `user.routes.ts` (63.33%) + - [ ] Test profile updates + - [ ] Test address management + - [ ] Test settings updates + +## Implementation Strategy + +1. **Setup Improvements** + - [ ] Create test utilities for common operations + - [ ] Set up test database configuration + - [ ] Create mock factories for test data + - [ ] Improve test isolation + +2. **Testing Approach** + - Use integration tests for routes + - Use unit tests for utilities and helpers + - Use mock data for external services + - Focus on error cases and edge conditions + +3. **Mocking Strategy** + - [ ] Create mock implementations for: + - Database operations + - External APIs (Cloudinary, OAuth providers) + - Email/SMS services + - Payment gateways + +4. **CI/CD Integration** + - [ ] Add coverage reporting to CI pipeline + - [ ] Set up coverage thresholds + - [ ] Add test status badges to README + +## Long-term Goals + +1. Achieve and maintain: + - 80% overall coverage + - 80% branch coverage + - 80% function coverage + - 80% line coverage + +2. Implement: + - End-to-end tests for critical flows + - Performance tests for key endpoints + - Security tests for authentication/authorization + - Load tests for high-traffic endpoints + +## Notes + +- Focus on business-critical paths first +- Prioritize security-related functionality +- Document test patterns and best practices +- Regular review and updates of test coverage +- Consider adding property-based testing for complex logic \ No newline at end of file diff --git a/TROUBLESHOOTING.md b/TROUBLESHOOTING.md index 58790d6..8a66c77 100644 --- a/TROUBLESHOOTING.md +++ b/TROUBLESHOOTING.md @@ -1,25 +1,224 @@ # Troubleshooting Guide -This file records important problems encountered in the project and how they were solved. Add new entries as needed. +This document lists common issues encountered during development and their solutions. ---- +## Table of Contents +- [Environment Setup Issues](#environment-setup-issues) +- [Docker Issues](#docker-issues) +- [Database Issues](#database-issues) +- [Authentication Issues](#authentication-issues) +- [Code Quality Issues](#code-quality-issues) +- [Testing Issues](#testing-issues) -## Docker Compose: App can't connect to Postgres -- **Symptom:** App fails to connect to the database when running with Docker Compose. -- **Solution:** Ensure the `DB_HOST`/`PROD_DB_HOSTNAME` is set to `db` in your environment. The `depends_on` field in `docker-compose.yml` ensures the database starts first, but you may need to add a wait-for-it script for production reliability. +## Environment Setup Issues -## Prettier Pre-push Hook Not Running -- **Symptom:** Code is not formatted before push. -- **Solution:** Make sure Husky is installed and `.husky/pre-push` is executable. Run `yarn husky install` if needed. +### Missing Environment Variables +**Problem**: Application fails to start due to missing environment variables. +**Solution**: +1. Copy `.env.example` to create a new `.env` file +2. Fill in all required variables: + ```env + NODE_ENV=development + PORT=3000 + + # Database Configuration + DB_HOST=localhost + DB_PORT=5432 + DB_USER=postgres + DB_PASSWORD=postgres + DB_NAME=farming_products + + # JWT Configuration + JWT_SECRET=your_jwt_secret + JWT_SECRET_REFRESH=your_jwt_refresh_secret + ``` -## Zod Validation Errors -- **Symptom:** API returns 400 with `errors` object. -- **Solution:** Check the error details in the response. Update your request body to match the required schema (see Swagger docs for examples). +### Node.js Version Compatibility +**Problem**: Package installation fails due to Node.js version mismatch. +**Solution**: +1. Use Node.js version 20 or later +2. Run `nvm use 20` if using nvm +3. Delete `node_modules` and `yarn.lock` +4. Run `yarn install` again -## Swagger UI Not Updating -- **Symptom:** Changes to Swagger docs are not reflected at `/api-docs`. -- **Solution:** Restart the server after making changes to route files or Swagger comments. Swagger UI reads docs at startup. +## Docker Issues ---- +### Docker Compose Database Connection +**Problem**: Application container cannot connect to database container. +**Solution**: +1. Ensure correct service names in `docker-compose.yml` +2. Use service name as host in database connection: + ```yaml + environment: + DB_HOST: db + DB_PORT: 5432 + ``` -Add more issues and solutions as you encounter them! \ No newline at end of file +### Docker Build Failures +**Problem**: Docker build fails due to TypeScript compilation errors. +**Solution**: +1. Use multi-stage builds to separate build and runtime environments +2. Include all dependencies (including devDependencies) in builder stage +3. Copy only necessary files to production stage: + ```dockerfile + FROM node:20-alpine AS builder + WORKDIR /app + COPY package.json yarn.lock ./ + RUN yarn install --frozen-lockfile + COPY . . + RUN yarn build + + FROM node:20-alpine AS prod + WORKDIR /app + COPY --from=builder /app/package.json ./ + COPY --from=builder /app/yarn.lock ./ + COPY --from=builder /app/node_modules ./node_modules + COPY --from=builder /app/dist ./dist + ``` + +## Database Issues + +### Migration Failures +**Problem**: Database migrations fail to run automatically. +**Solution**: +1. Set `RUN_MIGRATIONS=true` in environment variables +2. Ensure migrations are compiled to JavaScript in the dist folder +3. Add migration command to startup: + ```yaml + command: sh -c "yarn install && yarn build && npx sequelize-cli db:migrate && node dist/app.js" + ``` + +### Database Connection Timeouts +**Problem**: Application fails to connect to database on startup. +**Solution**: +1. Add retry logic in database connection +2. Ensure correct database credentials in environment variables +3. Check if database service is ready before connecting + +## Authentication Issues + +### JWT Token Verification +**Problem**: JWT verification fails with "invalid signature". +**Solution**: +1. Ensure same JWT secret is used for signing and verification +2. Check token expiration time +3. Verify token format in Authorization header: + ```typescript + // Correct format + Authorization: Bearer + ``` + +### OAuth Integration +**Problem**: Google/Facebook authentication fails. +**Solution**: +1. Verify OAuth credentials in environment variables +2. Ensure correct redirect URIs in OAuth provider settings +3. Handle token verification properly: + ```typescript + const ticket = await googleClient.verifyIdToken({ + idToken: googleToken, + audience: process.env.GMAIL_AUTH_CLIENTID, + }); + ``` + +## Code Quality Issues + +### ESLint Errors + +#### Unused Variables +**Problem**: ESLint error for unused function parameters. +**Solution**: +1. Prefix unused parameters with underscore +2. Update ESLint configuration: + ```json + { + "rules": { + "@typescript-eslint/no-unused-vars": [ + "error", + { "argsIgnorePattern": "^_" } + ] + } + } + ``` + +#### Express Error Handler +**Problem**: Unused `next` parameter in error handler. +**Solution**: +1. Remove unused parameter if not needed +2. Or prefix with underscore if required by type definition: + ```typescript + const errorHandler = ( + err: Error | AppError, + _req: Request, + res: Response + ): void => { + // Handler implementation + }; + ``` + +### TypeScript Type Issues +**Problem**: Type errors in request handlers. +**Solution**: +1. Define proper interfaces for request bodies +2. Use type assertions carefully +3. Implement proper type checking: + ```typescript + interface LoginRequest { + email: string; + password: string; + } + + app.post('/login', (req: Request, res: Response) => { + const { email, password } = req.body; + // Implementation + }); + ``` + +## Testing Issues + +### Test Environment Setup +**Problem**: Tests fail due to missing environment variables. +**Solution**: +1. Create `tests/setup.ts` for test environment configuration +2. Set test-specific environment variables: + ```typescript + process.env.NODE_ENV = 'test'; + process.env.JWT_SECRET = 'test_secret'; + ``` + +### Database Mocking +**Problem**: Tests fail due to database operations. +**Solution**: +1. Create mock implementations for database models +2. Use Vitest for mocking: + ```typescript + vi.mock('../src/models', () => ({ + User: { + findOne: vi.fn(), + create: vi.fn(), + } + })); + ``` + +### API Testing +**Problem**: Integration tests fail due to authentication. +**Solution**: +1. Create test utilities for generating valid tokens +2. Mock authentication middleware in tests +3. Use supertest for API testing: + ```typescript + import request from 'supertest'; + import app from '../app'; + + describe('Auth endpoints', () => { + it('should login successfully', async () => { + const response = await request(app) + .post('/api/v1/auth/login') + .send({ + email: 'test@example.com', + password: 'password123' + }); + expect(response.status).toBe(200); + }); + }); + ``` \ No newline at end of file diff --git a/app.ts b/app.ts index ba4163e..ca89e10 100644 --- a/app.ts +++ b/app.ts @@ -1,6 +1,6 @@ // app.ts import dotenv from "dotenv"; -import express, { Application, Request, Response } from "express"; +import express, { Application, Request, Response, NextFunction } from "express"; import bodyParser from "body-parser"; import limiter from "./src/middleware/rateLimiter"; import appRouter from "./src/routes"; @@ -11,6 +11,7 @@ import errorHandler from "./src/middleware/errorHandler"; import swaggerUi from "swagger-ui-express"; import swaggerJsDoc from "swagger-jsdoc"; import helmet from "helmet"; +import AppError from "./src/errors/customErrors"; // Define the server configuration const port: number = parseInt(process.env.PORT || "3000", 10); @@ -112,7 +113,6 @@ app.use("/api/v1", appRouter); // @ts-ignore-next-line app.use("/api-docs", swaggerUi.serve, swaggerUi.setup(swaggerSpec)); - // Swagger default route definition /** * @swagger @@ -136,18 +136,24 @@ app.get("/", (req: Request, res: Response) => { res.send("Hello World!! Farming products_2"); }); -// Error handling middleware -app.use(errorHandler); - - +// 404 handler +app.use((req: Request, res: Response, next: NextFunction) => { + next(new AppError(`Not Found - ${req.originalUrl}`, 404)); +}); -app.listen(port, async () => { +// Error handling middleware - must be last +app.use(errorHandler); - try { - console.log(`Server running on http${!isDev ? 's' : ''}://${!isDev ? process.env.DB_HOST : hostname}:${port} `); - } catch (error) { - console.error("Error running migrations:", error); - } -}); +// Only start the server if we're not in a test environment +if (process.env.NODE_ENV !== 'test') { + app.listen(port, async () => { + try { + console.log("✅ Database is up to date."); + } catch (error) { + console.error("❌ Unable to connect to the database:", error); + } + console.log(`Server running on http://localhost:${port}`); + }); +} export default app; diff --git a/coverage/base.css b/coverage/base.css new file mode 100644 index 0000000..f418035 --- /dev/null +++ b/coverage/base.css @@ -0,0 +1,224 @@ +body, html { + margin:0; padding: 0; + height: 100%; +} +body { + font-family: Helvetica Neue, Helvetica, Arial; + font-size: 14px; + color:#333; +} +.small { font-size: 12px; } +*, *:after, *:before { + -webkit-box-sizing:border-box; + -moz-box-sizing:border-box; + box-sizing:border-box; + } +h1 { font-size: 20px; margin: 0;} +h2 { font-size: 14px; } +pre { + font: 12px/1.4 Consolas, "Liberation Mono", Menlo, Courier, monospace; + margin: 0; + padding: 0; + -moz-tab-size: 2; + -o-tab-size: 2; + tab-size: 2; +} +a { color:#0074D9; text-decoration:none; } +a:hover { text-decoration:underline; } +.strong { font-weight: bold; } +.space-top1 { padding: 10px 0 0 0; } +.pad2y { padding: 20px 0; } +.pad1y { padding: 10px 0; } +.pad2x { padding: 0 20px; } +.pad2 { padding: 20px; } +.pad1 { padding: 10px; } +.space-left2 { padding-left:55px; } +.space-right2 { padding-right:20px; } +.center { text-align:center; } +.clearfix { display:block; } +.clearfix:after { + content:''; + display:block; + height:0; + clear:both; + visibility:hidden; + } +.fl { float: left; } +@media only screen and (max-width:640px) { + .col3 { width:100%; max-width:100%; } + .hide-mobile { display:none!important; } +} + +.quiet { + color: #7f7f7f; + color: rgba(0,0,0,0.5); +} +.quiet a { opacity: 0.7; } + +.fraction { + font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; + font-size: 10px; + color: #555; + background: #E8E8E8; + padding: 4px 5px; + border-radius: 3px; + vertical-align: middle; +} + +div.path a:link, div.path a:visited { color: #333; } +table.coverage { + border-collapse: collapse; + margin: 10px 0 0 0; + padding: 0; +} + +table.coverage td { + margin: 0; + padding: 0; + vertical-align: top; +} +table.coverage td.line-count { + text-align: right; + padding: 0 5px 0 20px; +} +table.coverage td.line-coverage { + text-align: right; + padding-right: 10px; + min-width:20px; +} + +table.coverage td span.cline-any { + display: inline-block; + padding: 0 5px; + width: 100%; +} +.missing-if-branch { + display: inline-block; + margin-right: 5px; + border-radius: 3px; + position: relative; + padding: 0 4px; + background: #333; + color: yellow; +} + +.skip-if-branch { + display: none; + margin-right: 10px; + position: relative; + padding: 0 4px; + background: #ccc; + color: white; +} +.missing-if-branch .typ, .skip-if-branch .typ { + color: inherit !important; +} +.coverage-summary { + border-collapse: collapse; + width: 100%; +} +.coverage-summary tr { border-bottom: 1px solid #bbb; } +.keyline-all { border: 1px solid #ddd; } +.coverage-summary td, .coverage-summary th { padding: 10px; } +.coverage-summary tbody { border: 1px solid #bbb; } +.coverage-summary td { border-right: 1px solid #bbb; } +.coverage-summary td:last-child { border-right: none; } +.coverage-summary th { + text-align: left; + font-weight: normal; + white-space: nowrap; +} +.coverage-summary th.file { border-right: none !important; } +.coverage-summary th.pct { } +.coverage-summary th.pic, +.coverage-summary th.abs, +.coverage-summary td.pct, +.coverage-summary td.abs { text-align: right; } +.coverage-summary td.file { white-space: nowrap; } +.coverage-summary td.pic { min-width: 120px !important; } +.coverage-summary tfoot td { } + +.coverage-summary .sorter { + height: 10px; + width: 7px; + display: inline-block; + margin-left: 0.5em; + background: url(sort-arrow-sprite.png) no-repeat scroll 0 0 transparent; +} +.coverage-summary .sorted .sorter { + background-position: 0 -20px; +} +.coverage-summary .sorted-desc .sorter { + background-position: 0 -10px; +} +.status-line { height: 10px; } +/* yellow */ +.cbranch-no { background: yellow !important; color: #111; } +/* dark red */ +.red.solid, .status-line.low, .low .cover-fill { background:#C21F39 } +.low .chart { border:1px solid #C21F39 } +.highlighted, +.highlighted .cstat-no, .highlighted .fstat-no, .highlighted .cbranch-no{ + background: #C21F39 !important; +} +/* medium red */ +.cstat-no, .fstat-no, .cbranch-no, .cbranch-no { background:#F6C6CE } +/* light red */ +.low, .cline-no { background:#FCE1E5 } +/* light green */ +.high, .cline-yes { background:rgb(230,245,208) } +/* medium green */ +.cstat-yes { background:rgb(161,215,106) } +/* dark green */ +.status-line.high, .high .cover-fill { background:rgb(77,146,33) } +.high .chart { border:1px solid rgb(77,146,33) } +/* dark yellow (gold) */ +.status-line.medium, .medium .cover-fill { background: #f9cd0b; } +.medium .chart { border:1px solid #f9cd0b; } +/* light yellow */ +.medium { background: #fff4c2; } + +.cstat-skip { background: #ddd; color: #111; } +.fstat-skip { background: #ddd; color: #111 !important; } +.cbranch-skip { background: #ddd !important; color: #111; } + +span.cline-neutral { background: #eaeaea; } + +.coverage-summary td.empty { + opacity: .5; + padding-top: 4px; + padding-bottom: 4px; + line-height: 1; + color: #888; +} + +.cover-fill, .cover-empty { + display:inline-block; + height: 12px; +} +.chart { + line-height: 0; +} +.cover-empty { + background: white; +} +.cover-full { + border-right: none !important; +} +pre.prettyprint { + border: none !important; + padding: 0 !important; + margin: 0 !important; +} +.com { color: #999 !important; } +.ignore-none { color: #999; font-weight: normal; } + +.wrapper { + min-height: 100%; + height: auto !important; + height: 100%; + margin: 0 auto -48px; +} +.footer, .push { + height: 48px; +} diff --git a/coverage/block-navigation.js b/coverage/block-navigation.js new file mode 100644 index 0000000..cc12130 --- /dev/null +++ b/coverage/block-navigation.js @@ -0,0 +1,87 @@ +/* eslint-disable */ +var jumpToCode = (function init() { + // Classes of code we would like to highlight in the file view + var missingCoverageClasses = ['.cbranch-no', '.cstat-no', '.fstat-no']; + + // Elements to highlight in the file listing view + var fileListingElements = ['td.pct.low']; + + // We don't want to select elements that are direct descendants of another match + var notSelector = ':not(' + missingCoverageClasses.join('):not(') + ') > '; // becomes `:not(a):not(b) > ` + + // Selecter that finds elements on the page to which we can jump + var selector = + fileListingElements.join(', ') + + ', ' + + notSelector + + missingCoverageClasses.join(', ' + notSelector); // becomes `:not(a):not(b) > a, :not(a):not(b) > b` + + // The NodeList of matching elements + var missingCoverageElements = document.querySelectorAll(selector); + + var currentIndex; + + function toggleClass(index) { + missingCoverageElements + .item(currentIndex) + .classList.remove('highlighted'); + missingCoverageElements.item(index).classList.add('highlighted'); + } + + function makeCurrent(index) { + toggleClass(index); + currentIndex = index; + missingCoverageElements.item(index).scrollIntoView({ + behavior: 'smooth', + block: 'center', + inline: 'center' + }); + } + + function goToPrevious() { + var nextIndex = 0; + if (typeof currentIndex !== 'number' || currentIndex === 0) { + nextIndex = missingCoverageElements.length - 1; + } else if (missingCoverageElements.length > 1) { + nextIndex = currentIndex - 1; + } + + makeCurrent(nextIndex); + } + + function goToNext() { + var nextIndex = 0; + + if ( + typeof currentIndex === 'number' && + currentIndex < missingCoverageElements.length - 1 + ) { + nextIndex = currentIndex + 1; + } + + makeCurrent(nextIndex); + } + + return function jump(event) { + if ( + document.getElementById('fileSearch') === document.activeElement && + document.activeElement != null + ) { + // if we're currently focused on the search input, we don't want to navigate + return; + } + + switch (event.which) { + case 78: // n + case 74: // j + goToNext(); + break; + case 66: // b + case 75: // k + case 80: // p + goToPrevious(); + break; + } + }; +})(); +window.addEventListener('keydown', jumpToCode); diff --git a/coverage/coverage-final.json b/coverage/coverage-final.json new file mode 100644 index 0000000..0881156 --- /dev/null +++ b/coverage/coverage-final.json @@ -0,0 +1,37 @@ +{"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/app.ts": {"path":"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/app.ts","all":false,"statementMap":{"1":{"start":{"line":2,"column":0},"end":{"line":2,"column":28}},"2":{"start":{"line":3,"column":0},"end":{"line":3,"column":80}},"3":{"start":{"line":4,"column":0},"end":{"line":4,"column":37}},"4":{"start":{"line":5,"column":0},"end":{"line":5,"column":51}},"5":{"start":{"line":6,"column":0},"end":{"line":6,"column":37}},"6":{"start":{"line":7,"column":0},"end":{"line":7,"column":24}},"7":{"start":{"line":8,"column":0},"end":{"line":8,"column":24}},"8":{"start":{"line":9,"column":0},"end":{"line":9,"column":20}},"9":{"start":{"line":10,"column":0},"end":{"line":10,"column":57}},"10":{"start":{"line":11,"column":0},"end":{"line":11,"column":43}},"11":{"start":{"line":12,"column":0},"end":{"line":12,"column":41}},"12":{"start":{"line":13,"column":0},"end":{"line":13,"column":28}},"13":{"start":{"line":14,"column":0},"end":{"line":14,"column":49}},"16":{"start":{"line":17,"column":0},"end":{"line":17,"column":62}},"17":{"start":{"line":18,"column":0},"end":{"line":18,"column":60}},"19":{"start":{"line":20,"column":0},"end":{"line":20,"column":52}},"21":{"start":{"line":22,"column":0},"end":{"line":22,"column":16}},"23":{"start":{"line":24,"column":0},"end":{"line":24,"column":35}},"26":{"start":{"line":27,"column":0},"end":{"line":27,"column":18}},"27":{"start":{"line":28,"column":0},"end":{"line":28,"column":15}},"28":{"start":{"line":29,"column":0},"end":{"line":29,"column":21}},"29":{"start":{"line":30,"column":0},"end":{"line":30,"column":11}},"30":{"start":{"line":31,"column":0},"end":{"line":31,"column":36}},"31":{"start":{"line":32,"column":0},"end":{"line":32,"column":23}},"32":{"start":{"line":33,"column":0},"end":{"line":33,"column":60}},"33":{"start":{"line":34,"column":0},"end":{"line":34,"column":16}},"34":{"start":{"line":35,"column":0},"end":{"line":35,"column":33}},"35":{"start":{"line":36,"column":0},"end":{"line":36,"column":7}},"36":{"start":{"line":37,"column":0},"end":{"line":37,"column":6}},"37":{"start":{"line":38,"column":0},"end":{"line":38,"column":14}},"38":{"start":{"line":39,"column":0},"end":{"line":39,"column":7}},"39":{"start":{"line":40,"column":0},"end":{"line":40,"column":44}},"40":{"start":{"line":41,"column":0},"end":{"line":41,"column":51}},"41":{"start":{"line":42,"column":0},"end":{"line":42,"column":7}},"42":{"start":{"line":43,"column":0},"end":{"line":43,"column":6}},"43":{"start":{"line":44,"column":0},"end":{"line":44,"column":17}},"44":{"start":{"line":45,"column":0},"end":{"line":45,"column":24}},"45":{"start":{"line":46,"column":0},"end":{"line":46,"column":21}},"46":{"start":{"line":47,"column":0},"end":{"line":47,"column":23}},"47":{"start":{"line":48,"column":0},"end":{"line":48,"column":27}},"48":{"start":{"line":49,"column":0},"end":{"line":49,"column":30}},"49":{"start":{"line":50,"column":0},"end":{"line":50,"column":10}},"50":{"start":{"line":51,"column":0},"end":{"line":51,"column":8}},"51":{"start":{"line":52,"column":0},"end":{"line":52,"column":16}},"53":{"start":{"line":54,"column":0},"end":{"line":54,"column":18}},"54":{"start":{"line":55,"column":0},"end":{"line":55,"column":25}},"55":{"start":{"line":56,"column":0},"end":{"line":56,"column":72}},"56":{"start":{"line":57,"column":0},"end":{"line":57,"column":23}},"57":{"start":{"line":58,"column":0},"end":{"line":58,"column":44}},"58":{"start":{"line":59,"column":0},"end":{"line":59,"column":43}},"59":{"start":{"line":60,"column":0},"end":{"line":60,"column":42}},"60":{"start":{"line":61,"column":0},"end":{"line":61,"column":38}},"61":{"start":{"line":62,"column":0},"end":{"line":62,"column":44}},"62":{"start":{"line":63,"column":0},"end":{"line":63,"column":43}},"63":{"start":{"line":64,"column":0},"end":{"line":64,"column":41}},"64":{"start":{"line":65,"column":0},"end":{"line":65,"column":39}},"65":{"start":{"line":66,"column":0},"end":{"line":66,"column":12}},"66":{"start":{"line":67,"column":0},"end":{"line":67,"column":20}},"67":{"start":{"line":68,"column":0},"end":{"line":68,"column":34}},"68":{"start":{"line":69,"column":0},"end":{"line":69,"column":36}},"69":{"start":{"line":70,"column":0},"end":{"line":70,"column":32}},"70":{"start":{"line":71,"column":0},"end":{"line":71,"column":24}},"71":{"start":{"line":72,"column":0},"end":{"line":72,"column":42}},"72":{"start":{"line":73,"column":0},"end":{"line":73,"column":29}},"73":{"start":{"line":74,"column":0},"end":{"line":74,"column":57}},"74":{"start":{"line":75,"column":0},"end":{"line":75,"column":31}},"75":{"start":{"line":76,"column":0},"end":{"line":76,"column":11}},"76":{"start":{"line":77,"column":0},"end":{"line":77,"column":10}},"78":{"start":{"line":79,"column":0},"end":{"line":79,"column":8}},"79":{"start":{"line":80,"column":0},"end":{"line":80,"column":6}},"80":{"start":{"line":81,"column":0},"end":{"line":81,"column":15}},"81":{"start":{"line":82,"column":0},"end":{"line":82,"column":7}},"82":{"start":{"line":83,"column":0},"end":{"line":83,"column":23}},"83":{"start":{"line":84,"column":0},"end":{"line":84,"column":8}},"84":{"start":{"line":85,"column":0},"end":{"line":85,"column":6}},"85":{"start":{"line":86,"column":0},"end":{"line":86,"column":4}},"86":{"start":{"line":87,"column":0},"end":{"line":87,"column":62}},"87":{"start":{"line":88,"column":0},"end":{"line":88,"column":2}},"90":{"start":{"line":91,"column":0},"end":{"line":91,"column":38}},"93":{"start":{"line":94,"column":0},"end":{"line":94,"column":61}},"96":{"start":{"line":97,"column":0},"end":{"line":97,"column":32}},"97":{"start":{"line":98,"column":0},"end":{"line":98,"column":47}},"98":{"start":{"line":99,"column":0},"end":{"line":99,"column":1}},"101":{"start":{"line":102,"column":0},"end":{"line":102,"column":17}},"102":{"start":{"line":103,"column":0},"end":{"line":103,"column":26}},"103":{"start":{"line":104,"column":0},"end":{"line":104,"column":18}},"105":{"start":{"line":106,"column":0},"end":{"line":106,"column":27}},"106":{"start":{"line":107,"column":0},"end":{"line":107,"column":16}},"107":{"start":{"line":108,"column":0},"end":{"line":108,"column":59}},"110":{"start":{"line":111,"column":0},"end":{"line":111,"column":30}},"113":{"start":{"line":114,"column":0},"end":{"line":114,"column":68}},"134":{"start":{"line":135,"column":0},"end":{"line":135,"column":47}},"135":{"start":{"line":136,"column":0},"end":{"line":136,"column":47}},"136":{"start":{"line":137,"column":0},"end":{"line":137,"column":3}},"139":{"start":{"line":140,"column":0},"end":{"line":140,"column":62}},"140":{"start":{"line":141,"column":0},"end":{"line":141,"column":60}},"141":{"start":{"line":142,"column":0},"end":{"line":142,"column":3}},"144":{"start":{"line":145,"column":0},"end":{"line":145,"column":22}},"147":{"start":{"line":148,"column":0},"end":{"line":148,"column":38}},"148":{"start":{"line":149,"column":0},"end":{"line":149,"column":32}},"149":{"start":{"line":150,"column":0},"end":{"line":150,"column":9}},"150":{"start":{"line":151,"column":0},"end":{"line":151,"column":47}},"151":{"start":{"line":152,"column":0},"end":{"line":152,"column":21}},"152":{"start":{"line":153,"column":0},"end":{"line":153,"column":67}},"153":{"start":{"line":154,"column":0},"end":{"line":154,"column":5}},"154":{"start":{"line":155,"column":0},"end":{"line":155,"column":62}},"155":{"start":{"line":156,"column":0},"end":{"line":156,"column":5}},"156":{"start":{"line":157,"column":0},"end":{"line":157,"column":1}},"158":{"start":{"line":159,"column":0},"end":{"line":159,"column":19}}},"s":{"1":1,"2":1,"3":1,"4":1,"5":1,"6":1,"7":1,"8":1,"9":1,"10":1,"11":1,"12":1,"13":1,"16":1,"17":1,"19":1,"21":1,"23":1,"26":1,"27":1,"28":1,"29":1,"30":1,"31":1,"32":1,"33":1,"34":1,"35":1,"36":1,"37":1,"38":1,"39":1,"40":1,"41":1,"42":1,"43":1,"44":1,"45":1,"46":1,"47":1,"48":1,"49":1,"50":1,"51":1,"53":1,"54":1,"55":1,"56":1,"57":1,"58":1,"59":1,"60":1,"61":1,"62":1,"63":1,"64":1,"65":1,"66":1,"67":1,"68":1,"69":1,"70":1,"71":1,"72":1,"73":1,"74":1,"75":1,"76":1,"78":1,"79":1,"80":1,"81":1,"82":1,"83":1,"84":1,"85":1,"86":1,"87":1,"90":1,"93":1,"96":1,"97":0,"98":0,"101":1,"102":1,"103":1,"105":1,"106":1,"107":1,"110":1,"113":1,"134":1,"135":0,"136":0,"139":1,"140":1,"141":1,"144":1,"147":1,"148":0,"149":0,"150":0,"151":0,"152":0,"153":0,"154":0,"155":0,"156":0,"158":1},"branchMap":{"0":{"type":"branch","line":87,"loc":{"start":{"line":87,"column":36},"end":{"line":87,"column":62}},"locations":[{"start":{"line":87,"column":36},"end":{"line":87,"column":62}}]},"1":{"type":"branch","line":97,"loc":{"start":{"line":97,"column":31},"end":{"line":99,"column":1}},"locations":[{"start":{"line":97,"column":31},"end":{"line":99,"column":1}}]},"2":{"type":"branch","line":148,"loc":{"start":{"line":148,"column":37},"end":{"line":157,"column":1}},"locations":[{"start":{"line":148,"column":37},"end":{"line":157,"column":1}}]},"3":{"type":"branch","line":140,"loc":{"start":{"line":140,"column":8},"end":{"line":142,"column":3}},"locations":[{"start":{"line":140,"column":8},"end":{"line":142,"column":3}}]}},"b":{"0":[0],"1":[0],"2":[0],"3":[1]},"fnMap":{},"f":{}} +,"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/src/config/config.ts": {"path":"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/src/config/config.ts","all":true,"statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":28}},"2":{"start":{"line":3,"column":0},"end":{"line":3,"column":16}},"4":{"start":{"line":5,"column":0},"end":{"line":5,"column":16}},"5":{"start":{"line":6,"column":0},"end":{"line":6,"column":16}},"6":{"start":{"line":7,"column":0},"end":{"line":7,"column":38}},"7":{"start":{"line":8,"column":0},"end":{"line":8,"column":38}},"8":{"start":{"line":9,"column":0},"end":{"line":9,"column":34}},"9":{"start":{"line":10,"column":0},"end":{"line":10,"column":30}},"10":{"start":{"line":11,"column":0},"end":{"line":11,"column":24}},"11":{"start":{"line":12,"column":0},"end":{"line":12,"column":4}},"12":{"start":{"line":13,"column":0},"end":{"line":13,"column":9}},"13":{"start":{"line":14,"column":0},"end":{"line":14,"column":38}},"14":{"start":{"line":15,"column":0},"end":{"line":15,"column":38}},"15":{"start":{"line":16,"column":0},"end":{"line":16,"column":34}},"16":{"start":{"line":17,"column":0},"end":{"line":17,"column":30}},"17":{"start":{"line":18,"column":0},"end":{"line":18,"column":24}},"18":{"start":{"line":19,"column":0},"end":{"line":19,"column":4}},"19":{"start":{"line":20,"column":0},"end":{"line":20,"column":15}},"20":{"start":{"line":21,"column":0},"end":{"line":21,"column":38}},"21":{"start":{"line":22,"column":0},"end":{"line":22,"column":38}},"22":{"start":{"line":23,"column":0},"end":{"line":23,"column":34}},"23":{"start":{"line":24,"column":0},"end":{"line":24,"column":30}},"24":{"start":{"line":25,"column":0},"end":{"line":25,"column":24}},"25":{"start":{"line":26,"column":0},"end":{"line":26,"column":4}},"26":{"start":{"line":27,"column":0},"end":{"line":27,"column":2}}},"s":{"0":0,"2":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0},"branchMap":{"0":{"type":"branch","line":1,"loc":{"start":{"line":1,"column":632},"end":{"line":27,"column":2}},"locations":[{"start":{"line":1,"column":632},"end":{"line":27,"column":2}}]}},"b":{"0":[0]},"fnMap":{"0":{"name":"(empty-report)","decl":{"start":{"line":1,"column":632},"end":{"line":27,"column":2}},"loc":{"start":{"line":1,"column":632},"end":{"line":27,"column":2}},"line":1}},"f":{"0":0}} +,"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/src/controllers/auth.controller.ts": {"path":"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/src/controllers/auth.controller.ts","all":false,"statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":34}},"1":{"start":{"line":2,"column":0},"end":{"line":2,"column":34}},"2":{"start":{"line":3,"column":0},"end":{"line":3,"column":48}},"3":{"start":{"line":4,"column":0},"end":{"line":4,"column":45}},"4":{"start":{"line":5,"column":0},"end":{"line":5,"column":35}},"5":{"start":{"line":6,"column":0},"end":{"line":6,"column":31}},"6":{"start":{"line":7,"column":0},"end":{"line":7,"column":36}},"7":{"start":{"line":8,"column":0},"end":{"line":8,"column":46}},"8":{"start":{"line":9,"column":0},"end":{"line":9,"column":36}},"9":{"start":{"line":10,"column":0},"end":{"line":10,"column":51}},"10":{"start":{"line":11,"column":0},"end":{"line":11,"column":26}},"12":{"start":{"line":13,"column":0},"end":{"line":13,"column":34}},"13":{"start":{"line":14,"column":0},"end":{"line":14,"column":46}},"66":{"start":{"line":67,"column":0},"end":{"line":67,"column":19}},"67":{"start":{"line":68,"column":0},"end":{"line":68,"column":48}},"68":{"start":{"line":69,"column":0},"end":{"line":69,"column":42}},"69":{"start":{"line":70,"column":0},"end":{"line":70,"column":48}},"70":{"start":{"line":71,"column":0},"end":{"line":71,"column":3}},"72":{"start":{"line":73,"column":0},"end":{"line":73,"column":71}},"75":{"start":{"line":76,"column":0},"end":{"line":76,"column":59}},"78":{"start":{"line":79,"column":0},"end":{"line":79,"column":70}},"79":{"start":{"line":80,"column":0},"end":{"line":80,"column":14}},"80":{"start":{"line":81,"column":0},"end":{"line":81,"column":52}},"81":{"start":{"line":82,"column":0},"end":{"line":82,"column":53}},"82":{"start":{"line":83,"column":0},"end":{"line":83,"column":46}},"83":{"start":{"line":84,"column":0},"end":{"line":84,"column":16}},"84":{"start":{"line":85,"column":0},"end":{"line":85,"column":20}},"85":{"start":{"line":86,"column":0},"end":{"line":86,"column":13}},"86":{"start":{"line":87,"column":0},"end":{"line":87,"column":37}},"87":{"start":{"line":88,"column":0},"end":{"line":88,"column":45}},"88":{"start":{"line":89,"column":0},"end":{"line":89,"column":8}},"89":{"start":{"line":90,"column":0},"end":{"line":90,"column":7}},"91":{"start":{"line":92,"column":0},"end":{"line":92,"column":25}},"92":{"start":{"line":93,"column":0},"end":{"line":93,"column":62}},"93":{"start":{"line":94,"column":0},"end":{"line":94,"column":16}},"94":{"start":{"line":95,"column":0},"end":{"line":95,"column":35}},"95":{"start":{"line":96,"column":0},"end":{"line":96,"column":13}},"97":{"start":{"line":98,"column":0},"end":{"line":98,"column":30}},"98":{"start":{"line":99,"column":0},"end":{"line":99,"column":101}},"103":{"start":{"line":104,"column":0},"end":{"line":104,"column":6}},"105":{"start":{"line":106,"column":0},"end":{"line":106,"column":44}},"106":{"start":{"line":107,"column":0},"end":{"line":107,"column":3}},"108":{"start":{"line":109,"column":0},"end":{"line":109,"column":14}},"109":{"start":{"line":110,"column":0},"end":{"line":110,"column":18}},"110":{"start":{"line":111,"column":0},"end":{"line":111,"column":50}},"111":{"start":{"line":112,"column":0},"end":{"line":112,"column":58}},"112":{"start":{"line":113,"column":0},"end":{"line":113,"column":35}},"113":{"start":{"line":114,"column":0},"end":{"line":114,"column":78}},"114":{"start":{"line":115,"column":0},"end":{"line":115,"column":21}},"115":{"start":{"line":116,"column":0},"end":{"line":116,"column":6}},"117":{"start":{"line":118,"column":0},"end":{"line":118,"column":20}},"118":{"start":{"line":119,"column":0},"end":{"line":119,"column":21}},"119":{"start":{"line":120,"column":0},"end":{"line":120,"column":69}},"120":{"start":{"line":121,"column":0},"end":{"line":121,"column":16}},"121":{"start":{"line":122,"column":0},"end":{"line":122,"column":43}},"122":{"start":{"line":123,"column":0},"end":{"line":123,"column":8}},"123":{"start":{"line":124,"column":0},"end":{"line":124,"column":33}},"124":{"start":{"line":125,"column":0},"end":{"line":125,"column":6}},"126":{"start":{"line":127,"column":0},"end":{"line":127,"column":32}},"127":{"start":{"line":128,"column":0},"end":{"line":128,"column":3}},"128":{"start":{"line":129,"column":0},"end":{"line":129,"column":2}},"131":{"start":{"line":132,"column":0},"end":{"line":132,"column":34}},"132":{"start":{"line":133,"column":0},"end":{"line":133,"column":53}},"133":{"start":{"line":134,"column":0},"end":{"line":134,"column":16}},"134":{"start":{"line":135,"column":0},"end":{"line":135,"column":21}},"135":{"start":{"line":136,"column":0},"end":{"line":136,"column":6}},"136":{"start":{"line":137,"column":0},"end":{"line":137,"column":7}},"137":{"start":{"line":138,"column":0},"end":{"line":138,"column":70}},"140":{"start":{"line":141,"column":0},"end":{"line":141,"column":30}},"141":{"start":{"line":142,"column":0},"end":{"line":142,"column":52}},"142":{"start":{"line":143,"column":0},"end":{"line":143,"column":5}},"144":{"start":{"line":145,"column":0},"end":{"line":145,"column":30}},"145":{"start":{"line":146,"column":0},"end":{"line":146,"column":72}},"146":{"start":{"line":147,"column":0},"end":{"line":147,"column":5}},"148":{"start":{"line":149,"column":0},"end":{"line":149,"column":56}},"149":{"start":{"line":150,"column":0},"end":{"line":150,"column":55}},"150":{"start":{"line":151,"column":0},"end":{"line":151,"column":5}},"152":{"start":{"line":153,"column":0},"end":{"line":153,"column":52}},"153":{"start":{"line":154,"column":0},"end":{"line":154,"column":8}},"154":{"start":{"line":155,"column":0},"end":{"line":155,"column":18}},"155":{"start":{"line":156,"column":0},"end":{"line":156,"column":67}},"156":{"start":{"line":157,"column":0},"end":{"line":157,"column":7}},"157":{"start":{"line":158,"column":0},"end":{"line":158,"column":25}},"158":{"start":{"line":159,"column":0},"end":{"line":159,"column":79}},"159":{"start":{"line":160,"column":0},"end":{"line":160,"column":12}},"160":{"start":{"line":161,"column":0},"end":{"line":161,"column":8}},"161":{"start":{"line":162,"column":0},"end":{"line":162,"column":5}},"164":{"start":{"line":165,"column":0},"end":{"line":165,"column":64}},"165":{"start":{"line":166,"column":0},"end":{"line":166,"column":21}},"166":{"start":{"line":167,"column":0},"end":{"line":167,"column":67}},"167":{"start":{"line":168,"column":0},"end":{"line":168,"column":5}},"170":{"start":{"line":171,"column":0},"end":{"line":171,"column":69}},"171":{"start":{"line":172,"column":0},"end":{"line":172,"column":16}},"172":{"start":{"line":173,"column":0},"end":{"line":173,"column":55}},"173":{"start":{"line":174,"column":0},"end":{"line":174,"column":5}},"176":{"start":{"line":177,"column":0},"end":{"line":177,"column":57}},"179":{"start":{"line":180,"column":0},"end":{"line":180,"column":36}},"180":{"start":{"line":181,"column":0},"end":{"line":181,"column":22}},"181":{"start":{"line":182,"column":0},"end":{"line":182,"column":12}},"182":{"start":{"line":183,"column":0},"end":{"line":183,"column":31}},"183":{"start":{"line":184,"column":0},"end":{"line":184,"column":14}},"184":{"start":{"line":185,"column":0},"end":{"line":185,"column":44}},"185":{"start":{"line":186,"column":0},"end":{"line":186,"column":20}},"186":{"start":{"line":187,"column":0},"end":{"line":187,"column":19}},"187":{"start":{"line":188,"column":0},"end":{"line":188,"column":19}},"188":{"start":{"line":189,"column":0},"end":{"line":189,"column":18}},"189":{"start":{"line":190,"column":0},"end":{"line":190,"column":19}},"190":{"start":{"line":191,"column":0},"end":{"line":191,"column":26}},"191":{"start":{"line":192,"column":0},"end":{"line":192,"column":17}},"192":{"start":{"line":193,"column":0},"end":{"line":193,"column":21}},"193":{"start":{"line":194,"column":0},"end":{"line":194,"column":22}},"194":{"start":{"line":195,"column":0},"end":{"line":195,"column":28}},"195":{"start":{"line":196,"column":0},"end":{"line":196,"column":28}},"196":{"start":{"line":197,"column":0},"end":{"line":197,"column":7}},"198":{"start":{"line":199,"column":0},"end":{"line":199,"column":16}},"199":{"start":{"line":200,"column":0},"end":{"line":200,"column":75}},"200":{"start":{"line":201,"column":0},"end":{"line":201,"column":5}},"203":{"start":{"line":204,"column":0},"end":{"line":204,"column":30}},"204":{"start":{"line":205,"column":0},"end":{"line":205,"column":40}},"207":{"start":{"line":208,"column":0},"end":{"line":208,"column":45}},"208":{"start":{"line":209,"column":0},"end":{"line":209,"column":22}},"209":{"start":{"line":210,"column":0},"end":{"line":210,"column":21}},"210":{"start":{"line":211,"column":0},"end":{"line":211,"column":76}},"211":{"start":{"line":212,"column":0},"end":{"line":212,"column":28}},"212":{"start":{"line":213,"column":0},"end":{"line":213,"column":28}},"213":{"start":{"line":214,"column":0},"end":{"line":214,"column":7}},"215":{"start":{"line":216,"column":0},"end":{"line":216,"column":18}},"216":{"start":{"line":217,"column":0},"end":{"line":217,"column":70}},"217":{"start":{"line":218,"column":0},"end":{"line":218,"column":5}},"220":{"start":{"line":221,"column":0},"end":{"line":221,"column":40}},"223":{"start":{"line":224,"column":0},"end":{"line":224,"column":33}},"224":{"start":{"line":225,"column":0},"end":{"line":225,"column":62}},"225":{"start":{"line":226,"column":0},"end":{"line":226,"column":24}},"226":{"start":{"line":227,"column":0},"end":{"line":227,"column":30}},"227":{"start":{"line":228,"column":0},"end":{"line":228,"column":22}},"228":{"start":{"line":229,"column":0},"end":{"line":229,"column":7}},"229":{"start":{"line":230,"column":0},"end":{"line":230,"column":19}},"230":{"start":{"line":231,"column":0},"end":{"line":231,"column":33}},"231":{"start":{"line":232,"column":0},"end":{"line":232,"column":11}},"232":{"start":{"line":233,"column":0},"end":{"line":233,"column":21}},"233":{"start":{"line":234,"column":0},"end":{"line":234,"column":24}},"234":{"start":{"line":235,"column":0},"end":{"line":235,"column":61}},"235":{"start":{"line":236,"column":0},"end":{"line":236,"column":10}},"236":{"start":{"line":237,"column":0},"end":{"line":237,"column":8}},"237":{"start":{"line":238,"column":0},"end":{"line":238,"column":12}},"238":{"start":{"line":239,"column":0},"end":{"line":239,"column":62}},"239":{"start":{"line":240,"column":0},"end":{"line":240,"column":5}},"240":{"start":{"line":241,"column":0},"end":{"line":241,"column":3}},"241":{"start":{"line":242,"column":0},"end":{"line":242,"column":2}},"244":{"start":{"line":245,"column":0},"end":{"line":245,"column":36}},"245":{"start":{"line":246,"column":0},"end":{"line":246,"column":65}},"246":{"start":{"line":247,"column":0},"end":{"line":247,"column":16}},"247":{"start":{"line":248,"column":0},"end":{"line":248,"column":21}},"248":{"start":{"line":249,"column":0},"end":{"line":249,"column":6}},"249":{"start":{"line":250,"column":0},"end":{"line":250,"column":7}},"250":{"start":{"line":251,"column":0},"end":{"line":251,"column":16}},"251":{"start":{"line":252,"column":0},"end":{"line":252,"column":60}},"252":{"start":{"line":253,"column":0},"end":{"line":253,"column":34}},"253":{"start":{"line":254,"column":0},"end":{"line":254,"column":29}},"254":{"start":{"line":255,"column":0},"end":{"line":255,"column":18}},"255":{"start":{"line":256,"column":0},"end":{"line":256,"column":25}},"256":{"start":{"line":257,"column":0},"end":{"line":257,"column":23}},"257":{"start":{"line":258,"column":0},"end":{"line":258,"column":29}},"258":{"start":{"line":259,"column":0},"end":{"line":259,"column":22}},"259":{"start":{"line":260,"column":0},"end":{"line":260,"column":30}},"260":{"start":{"line":261,"column":0},"end":{"line":261,"column":30}},"261":{"start":{"line":262,"column":0},"end":{"line":262,"column":6}},"263":{"start":{"line":264,"column":0},"end":{"line":264,"column":34}},"264":{"start":{"line":265,"column":0},"end":{"line":265,"column":48}},"265":{"start":{"line":266,"column":0},"end":{"line":266,"column":48}},"266":{"start":{"line":267,"column":0},"end":{"line":267,"column":46}},"268":{"start":{"line":269,"column":0},"end":{"line":269,"column":45}},"269":{"start":{"line":270,"column":0},"end":{"line":270,"column":7}},"270":{"start":{"line":271,"column":0},"end":{"line":271,"column":21}},"271":{"start":{"line":272,"column":0},"end":{"line":272,"column":22}},"272":{"start":{"line":273,"column":0},"end":{"line":273,"column":16}},"273":{"start":{"line":274,"column":0},"end":{"line":274,"column":22}},"274":{"start":{"line":275,"column":0},"end":{"line":275,"column":8}},"275":{"start":{"line":276,"column":0},"end":{"line":276,"column":6}},"277":{"start":{"line":278,"column":0},"end":{"line":278,"column":22}},"278":{"start":{"line":279,"column":0},"end":{"line":279,"column":19}},"279":{"start":{"line":280,"column":0},"end":{"line":280,"column":69}},"280":{"start":{"line":281,"column":0},"end":{"line":281,"column":22}},"281":{"start":{"line":282,"column":0},"end":{"line":282,"column":8}},"282":{"start":{"line":283,"column":0},"end":{"line":283,"column":50}},"283":{"start":{"line":284,"column":0},"end":{"line":284,"column":5}},"285":{"start":{"line":286,"column":0},"end":{"line":286,"column":22}},"286":{"start":{"line":287,"column":0},"end":{"line":287,"column":7}},"287":{"start":{"line":288,"column":0},"end":{"line":288,"column":18}},"288":{"start":{"line":289,"column":0},"end":{"line":289,"column":17}},"289":{"start":{"line":290,"column":0},"end":{"line":290,"column":16}},"290":{"start":{"line":291,"column":0},"end":{"line":291,"column":17}},"291":{"start":{"line":292,"column":0},"end":{"line":292,"column":34}},"292":{"start":{"line":293,"column":0},"end":{"line":293,"column":22}},"293":{"start":{"line":294,"column":0},"end":{"line":294,"column":30}},"294":{"start":{"line":295,"column":0},"end":{"line":295,"column":8}},"295":{"start":{"line":296,"column":0},"end":{"line":296,"column":32}},"296":{"start":{"line":297,"column":0},"end":{"line":297,"column":6}},"298":{"start":{"line":299,"column":0},"end":{"line":299,"column":77}},"299":{"start":{"line":300,"column":0},"end":{"line":300,"column":19}},"300":{"start":{"line":301,"column":0},"end":{"line":301,"column":16}},"301":{"start":{"line":302,"column":0},"end":{"line":302,"column":3}},"302":{"start":{"line":303,"column":0},"end":{"line":303,"column":2}},"305":{"start":{"line":306,"column":0},"end":{"line":306,"column":32}},"306":{"start":{"line":307,"column":0},"end":{"line":307,"column":51}},"307":{"start":{"line":308,"column":0},"end":{"line":308,"column":16}},"308":{"start":{"line":309,"column":0},"end":{"line":309,"column":6}},"309":{"start":{"line":310,"column":0},"end":{"line":310,"column":34}},"311":{"start":{"line":312,"column":0},"end":{"line":312,"column":7}},"312":{"start":{"line":313,"column":0},"end":{"line":313,"column":65}},"313":{"start":{"line":314,"column":0},"end":{"line":314,"column":23}},"314":{"start":{"line":315,"column":0},"end":{"line":315,"column":16}},"315":{"start":{"line":316,"column":0},"end":{"line":316,"column":20}},"316":{"start":{"line":317,"column":0},"end":{"line":317,"column":71}},"317":{"start":{"line":318,"column":0},"end":{"line":318,"column":5}},"319":{"start":{"line":320,"column":0},"end":{"line":320,"column":46}},"320":{"start":{"line":321,"column":0},"end":{"line":321,"column":40}},"321":{"start":{"line":322,"column":0},"end":{"line":322,"column":7}},"322":{"start":{"line":323,"column":0},"end":{"line":323,"column":18}},"323":{"start":{"line":324,"column":0},"end":{"line":324,"column":35}},"324":{"start":{"line":325,"column":0},"end":{"line":325,"column":71}},"325":{"start":{"line":326,"column":0},"end":{"line":326,"column":9}},"326":{"start":{"line":327,"column":0},"end":{"line":327,"column":5}},"328":{"start":{"line":329,"column":0},"end":{"line":329,"column":33}},"329":{"start":{"line":330,"column":0},"end":{"line":330,"column":33}},"330":{"start":{"line":331,"column":0},"end":{"line":331,"column":71}},"331":{"start":{"line":332,"column":0},"end":{"line":332,"column":16}},"332":{"start":{"line":333,"column":0},"end":{"line":333,"column":20}},"333":{"start":{"line":334,"column":0},"end":{"line":334,"column":69}},"334":{"start":{"line":335,"column":0},"end":{"line":335,"column":5}},"336":{"start":{"line":337,"column":0},"end":{"line":337,"column":52}},"337":{"start":{"line":338,"column":0},"end":{"line":338,"column":19}},"338":{"start":{"line":339,"column":0},"end":{"line":339,"column":71}},"339":{"start":{"line":340,"column":0},"end":{"line":340,"column":70}},"341":{"start":{"line":342,"column":0},"end":{"line":342,"column":34}},"342":{"start":{"line":343,"column":0},"end":{"line":343,"column":16}},"343":{"start":{"line":344,"column":0},"end":{"line":344,"column":20}},"344":{"start":{"line":345,"column":0},"end":{"line":345,"column":77}},"345":{"start":{"line":346,"column":0},"end":{"line":346,"column":12}},"346":{"start":{"line":347,"column":0},"end":{"line":347,"column":60}},"347":{"start":{"line":348,"column":0},"end":{"line":348,"column":5}},"348":{"start":{"line":349,"column":0},"end":{"line":349,"column":19}},"349":{"start":{"line":350,"column":0},"end":{"line":350,"column":33}},"350":{"start":{"line":351,"column":0},"end":{"line":351,"column":35}},"351":{"start":{"line":352,"column":0},"end":{"line":352,"column":42}},"352":{"start":{"line":353,"column":0},"end":{"line":353,"column":29}},"353":{"start":{"line":354,"column":0},"end":{"line":354,"column":9}},"354":{"start":{"line":355,"column":0},"end":{"line":355,"column":5}},"355":{"start":{"line":356,"column":0},"end":{"line":356,"column":33}},"356":{"start":{"line":357,"column":0},"end":{"line":357,"column":40}},"357":{"start":{"line":358,"column":0},"end":{"line":358,"column":7}},"358":{"start":{"line":359,"column":0},"end":{"line":359,"column":3}},"359":{"start":{"line":360,"column":0},"end":{"line":360,"column":2}},"362":{"start":{"line":363,"column":0},"end":{"line":363,"column":28}},"363":{"start":{"line":364,"column":0},"end":{"line":364,"column":47}},"364":{"start":{"line":365,"column":0},"end":{"line":365,"column":16}},"365":{"start":{"line":366,"column":0},"end":{"line":366,"column":21}},"366":{"start":{"line":367,"column":0},"end":{"line":367,"column":6}},"367":{"start":{"line":368,"column":0},"end":{"line":368,"column":7}},"368":{"start":{"line":369,"column":0},"end":{"line":369,"column":41}},"370":{"start":{"line":371,"column":0},"end":{"line":371,"column":37}},"371":{"start":{"line":372,"column":0},"end":{"line":372,"column":23}},"372":{"start":{"line":373,"column":0},"end":{"line":373,"column":16}},"373":{"start":{"line":374,"column":0},"end":{"line":374,"column":9}},"374":{"start":{"line":375,"column":0},"end":{"line":375,"column":22}},"375":{"start":{"line":376,"column":0},"end":{"line":376,"column":35}},"376":{"start":{"line":377,"column":0},"end":{"line":377,"column":10}},"377":{"start":{"line":378,"column":0},"end":{"line":378,"column":8}},"378":{"start":{"line":379,"column":0},"end":{"line":379,"column":7}},"380":{"start":{"line":381,"column":0},"end":{"line":381,"column":16}},"381":{"start":{"line":382,"column":0},"end":{"line":382,"column":71}},"382":{"start":{"line":383,"column":0},"end":{"line":383,"column":5}},"384":{"start":{"line":385,"column":0},"end":{"line":385,"column":76}},"385":{"start":{"line":386,"column":0},"end":{"line":386,"column":26}},"386":{"start":{"line":387,"column":0},"end":{"line":387,"column":52}},"387":{"start":{"line":388,"column":0},"end":{"line":388,"column":5}},"389":{"start":{"line":390,"column":0},"end":{"line":390,"column":27}},"390":{"start":{"line":391,"column":0},"end":{"line":391,"column":7}},"391":{"start":{"line":392,"column":0},"end":{"line":392,"column":24}},"392":{"start":{"line":393,"column":0},"end":{"line":393,"column":26}},"393":{"start":{"line":394,"column":0},"end":{"line":394,"column":8}},"394":{"start":{"line":395,"column":0},"end":{"line":395,"column":39}},"395":{"start":{"line":396,"column":0},"end":{"line":396,"column":26}},"396":{"start":{"line":397,"column":0},"end":{"line":397,"column":6}},"398":{"start":{"line":399,"column":0},"end":{"line":399,"column":34}},"399":{"start":{"line":400,"column":0},"end":{"line":400,"column":7}},"400":{"start":{"line":401,"column":0},"end":{"line":401,"column":24}},"401":{"start":{"line":402,"column":0},"end":{"line":402,"column":26}},"402":{"start":{"line":403,"column":0},"end":{"line":403,"column":8}},"403":{"start":{"line":404,"column":0},"end":{"line":404,"column":47}},"404":{"start":{"line":405,"column":0},"end":{"line":405,"column":26}},"405":{"start":{"line":406,"column":0},"end":{"line":406,"column":6}},"407":{"start":{"line":408,"column":0},"end":{"line":408,"column":25}},"409":{"start":{"line":410,"column":0},"end":{"line":410,"column":26}},"410":{"start":{"line":411,"column":0},"end":{"line":411,"column":43}},"411":{"start":{"line":412,"column":0},"end":{"line":412,"column":12}},"412":{"start":{"line":413,"column":0},"end":{"line":413,"column":19}},"413":{"start":{"line":414,"column":0},"end":{"line":414,"column":21}},"414":{"start":{"line":415,"column":0},"end":{"line":415,"column":7}},"415":{"start":{"line":416,"column":0},"end":{"line":416,"column":19}},"416":{"start":{"line":417,"column":0},"end":{"line":417,"column":33}},"417":{"start":{"line":418,"column":0},"end":{"line":418,"column":11}},"418":{"start":{"line":419,"column":0},"end":{"line":419,"column":21}},"419":{"start":{"line":420,"column":0},"end":{"line":420,"column":24}},"420":{"start":{"line":421,"column":0},"end":{"line":421,"column":61}},"421":{"start":{"line":422,"column":0},"end":{"line":422,"column":10}},"422":{"start":{"line":423,"column":0},"end":{"line":423,"column":8}},"423":{"start":{"line":424,"column":0},"end":{"line":424,"column":12}},"424":{"start":{"line":425,"column":0},"end":{"line":425,"column":62}},"425":{"start":{"line":426,"column":0},"end":{"line":426,"column":5}},"426":{"start":{"line":427,"column":0},"end":{"line":427,"column":3}},"427":{"start":{"line":428,"column":0},"end":{"line":428,"column":2}},"430":{"start":{"line":431,"column":0},"end":{"line":431,"column":35}},"431":{"start":{"line":432,"column":0},"end":{"line":432,"column":15}},"432":{"start":{"line":433,"column":0},"end":{"line":433,"column":16}},"433":{"start":{"line":434,"column":0},"end":{"line":434,"column":21}},"434":{"start":{"line":435,"column":0},"end":{"line":435,"column":6}},"435":{"start":{"line":436,"column":0},"end":{"line":436,"column":7}},"436":{"start":{"line":437,"column":0},"end":{"line":437,"column":49}},"438":{"start":{"line":439,"column":0},"end":{"line":439,"column":59}},"439":{"start":{"line":440,"column":0},"end":{"line":440,"column":35}},"440":{"start":{"line":441,"column":0},"end":{"line":441,"column":76}},"441":{"start":{"line":442,"column":0},"end":{"line":442,"column":9}},"442":{"start":{"line":443,"column":0},"end":{"line":443,"column":5}},"445":{"start":{"line":446,"column":0},"end":{"line":446,"column":43}},"448":{"start":{"line":449,"column":0},"end":{"line":449,"column":36}},"449":{"start":{"line":450,"column":0},"end":{"line":450,"column":12}},"450":{"start":{"line":451,"column":0},"end":{"line":451,"column":47}},"451":{"start":{"line":452,"column":0},"end":{"line":452,"column":24}},"453":{"start":{"line":454,"column":0},"end":{"line":454,"column":28}},"454":{"start":{"line":455,"column":0},"end":{"line":455,"column":35}},"455":{"start":{"line":456,"column":0},"end":{"line":456,"column":20}},"456":{"start":{"line":457,"column":0},"end":{"line":457,"column":58}},"457":{"start":{"line":458,"column":0},"end":{"line":458,"column":9}},"458":{"start":{"line":459,"column":0},"end":{"line":459,"column":5}},"461":{"start":{"line":462,"column":0},"end":{"line":462,"column":33}},"462":{"start":{"line":463,"column":0},"end":{"line":463,"column":65}},"463":{"start":{"line":464,"column":0},"end":{"line":464,"column":39}},"464":{"start":{"line":465,"column":0},"end":{"line":465,"column":26}},"465":{"start":{"line":466,"column":0},"end":{"line":466,"column":6}},"467":{"start":{"line":468,"column":0},"end":{"line":468,"column":14}},"468":{"start":{"line":469,"column":0},"end":{"line":469,"column":18}},"469":{"start":{"line":470,"column":0},"end":{"line":470,"column":77}},"470":{"start":{"line":471,"column":0},"end":{"line":471,"column":19}},"471":{"start":{"line":472,"column":0},"end":{"line":472,"column":49}},"472":{"start":{"line":473,"column":0},"end":{"line":473,"column":16}},"473":{"start":{"line":474,"column":0},"end":{"line":474,"column":20}},"474":{"start":{"line":475,"column":0},"end":{"line":475,"column":64}},"475":{"start":{"line":476,"column":0},"end":{"line":476,"column":5}},"476":{"start":{"line":477,"column":0},"end":{"line":477,"column":16}},"477":{"start":{"line":478,"column":0},"end":{"line":478,"column":3}},"478":{"start":{"line":479,"column":0},"end":{"line":479,"column":2}},"481":{"start":{"line":482,"column":0},"end":{"line":482,"column":34}},"482":{"start":{"line":483,"column":0},"end":{"line":483,"column":53}},"483":{"start":{"line":484,"column":0},"end":{"line":484,"column":16}},"484":{"start":{"line":485,"column":0},"end":{"line":485,"column":21}},"485":{"start":{"line":486,"column":0},"end":{"line":486,"column":6}},"486":{"start":{"line":487,"column":0},"end":{"line":487,"column":50}},"488":{"start":{"line":489,"column":0},"end":{"line":489,"column":7}},"489":{"start":{"line":490,"column":0},"end":{"line":490,"column":41}},"490":{"start":{"line":491,"column":0},"end":{"line":491,"column":79}},"491":{"start":{"line":492,"column":0},"end":{"line":492,"column":5}},"494":{"start":{"line":495,"column":0},"end":{"line":495,"column":68}},"495":{"start":{"line":496,"column":0},"end":{"line":496,"column":16}},"496":{"start":{"line":497,"column":0},"end":{"line":497,"column":54}},"497":{"start":{"line":498,"column":0},"end":{"line":498,"column":5}},"499":{"start":{"line":500,"column":0},"end":{"line":500,"column":13}},"502":{"start":{"line":503,"column":0},"end":{"line":503,"column":22}},"503":{"start":{"line":504,"column":0},"end":{"line":504,"column":55}},"504":{"start":{"line":505,"column":0},"end":{"line":505,"column":29}},"505":{"start":{"line":506,"column":0},"end":{"line":506,"column":50}},"506":{"start":{"line":507,"column":0},"end":{"line":507,"column":9}},"508":{"start":{"line":509,"column":0},"end":{"line":509,"column":71}},"509":{"start":{"line":510,"column":0},"end":{"line":510,"column":21}},"510":{"start":{"line":511,"column":0},"end":{"line":511,"column":73}},"511":{"start":{"line":512,"column":0},"end":{"line":512,"column":7}},"513":{"start":{"line":514,"column":0},"end":{"line":514,"column":13}},"514":{"start":{"line":515,"column":0},"end":{"line":515,"column":22}},"515":{"start":{"line":516,"column":0},"end":{"line":516,"column":19}},"516":{"start":{"line":517,"column":0},"end":{"line":517,"column":35}},"517":{"start":{"line":518,"column":0},"end":{"line":518,"column":35}},"518":{"start":{"line":519,"column":0},"end":{"line":519,"column":31}},"519":{"start":{"line":520,"column":0},"end":{"line":520,"column":18}},"521":{"start":{"line":522,"column":0},"end":{"line":522,"column":54}},"523":{"start":{"line":524,"column":0},"end":{"line":524,"column":17}},"524":{"start":{"line":525,"column":0},"end":{"line":525,"column":29}},"525":{"start":{"line":526,"column":0},"end":{"line":526,"column":35}},"526":{"start":{"line":527,"column":0},"end":{"line":527,"column":28}},"527":{"start":{"line":528,"column":0},"end":{"line":528,"column":48}},"528":{"start":{"line":529,"column":0},"end":{"line":529,"column":20}},"529":{"start":{"line":530,"column":0},"end":{"line":530,"column":24}},"530":{"start":{"line":531,"column":0},"end":{"line":531,"column":76}},"531":{"start":{"line":532,"column":0},"end":{"line":532,"column":9}},"532":{"start":{"line":533,"column":0},"end":{"line":533,"column":14}},"533":{"start":{"line":534,"column":0},"end":{"line":534,"column":34}},"534":{"start":{"line":535,"column":0},"end":{"line":535,"column":19}},"535":{"start":{"line":536,"column":0},"end":{"line":536,"column":16}},"536":{"start":{"line":537,"column":0},"end":{"line":537,"column":20}},"537":{"start":{"line":538,"column":0},"end":{"line":538,"column":19}},"538":{"start":{"line":539,"column":0},"end":{"line":539,"column":19}},"539":{"start":{"line":540,"column":0},"end":{"line":540,"column":26}},"540":{"start":{"line":541,"column":0},"end":{"line":541,"column":26}},"541":{"start":{"line":542,"column":0},"end":{"line":542,"column":22}},"542":{"start":{"line":543,"column":0},"end":{"line":543,"column":22}},"543":{"start":{"line":544,"column":0},"end":{"line":544,"column":22}},"544":{"start":{"line":545,"column":0},"end":{"line":545,"column":21}},"545":{"start":{"line":546,"column":0},"end":{"line":546,"column":30}},"546":{"start":{"line":547,"column":0},"end":{"line":547,"column":25}},"547":{"start":{"line":548,"column":0},"end":{"line":548,"column":32}},"548":{"start":{"line":549,"column":0},"end":{"line":549,"column":32}},"549":{"start":{"line":550,"column":0},"end":{"line":550,"column":11}},"550":{"start":{"line":551,"column":0},"end":{"line":551,"column":7}},"551":{"start":{"line":552,"column":0},"end":{"line":552,"column":5}},"554":{"start":{"line":555,"column":0},"end":{"line":555,"column":29}},"555":{"start":{"line":556,"column":0},"end":{"line":556,"column":40}},"561":{"start":{"line":562,"column":0},"end":{"line":562,"column":99}},"562":{"start":{"line":563,"column":0},"end":{"line":563,"column":8}},"564":{"start":{"line":565,"column":0},"end":{"line":565,"column":60}},"566":{"start":{"line":567,"column":0},"end":{"line":567,"column":19}},"567":{"start":{"line":568,"column":0},"end":{"line":568,"column":18}},"568":{"start":{"line":569,"column":0},"end":{"line":569,"column":22}},"569":{"start":{"line":570,"column":0},"end":{"line":570,"column":68}},"570":{"start":{"line":571,"column":0},"end":{"line":571,"column":7}},"572":{"start":{"line":573,"column":0},"end":{"line":573,"column":42}},"573":{"start":{"line":574,"column":0},"end":{"line":574,"column":48}},"575":{"start":{"line":576,"column":0},"end":{"line":576,"column":54}},"577":{"start":{"line":578,"column":0},"end":{"line":578,"column":17}},"578":{"start":{"line":579,"column":0},"end":{"line":579,"column":31}},"579":{"start":{"line":580,"column":0},"end":{"line":580,"column":39}},"580":{"start":{"line":581,"column":0},"end":{"line":581,"column":28}},"581":{"start":{"line":582,"column":0},"end":{"line":582,"column":52}},"582":{"start":{"line":583,"column":0},"end":{"line":583,"column":20}},"583":{"start":{"line":584,"column":0},"end":{"line":584,"column":24}},"584":{"start":{"line":585,"column":0},"end":{"line":585,"column":76}},"585":{"start":{"line":586,"column":0},"end":{"line":586,"column":9}},"586":{"start":{"line":587,"column":0},"end":{"line":587,"column":14}},"587":{"start":{"line":588,"column":0},"end":{"line":588,"column":34}},"588":{"start":{"line":589,"column":0},"end":{"line":589,"column":21}},"589":{"start":{"line":590,"column":0},"end":{"line":590,"column":16}},"590":{"start":{"line":591,"column":0},"end":{"line":591,"column":20}},"591":{"start":{"line":592,"column":0},"end":{"line":592,"column":19}},"592":{"start":{"line":593,"column":0},"end":{"line":593,"column":26}},"593":{"start":{"line":594,"column":0},"end":{"line":594,"column":22}},"594":{"start":{"line":595,"column":0},"end":{"line":595,"column":22}},"595":{"start":{"line":596,"column":0},"end":{"line":596,"column":26}},"596":{"start":{"line":597,"column":0},"end":{"line":597,"column":23}},"597":{"start":{"line":598,"column":0},"end":{"line":598,"column":58}},"598":{"start":{"line":599,"column":0},"end":{"line":599,"column":21}},"599":{"start":{"line":600,"column":0},"end":{"line":600,"column":30}},"600":{"start":{"line":601,"column":0},"end":{"line":601,"column":22}},"601":{"start":{"line":602,"column":0},"end":{"line":602,"column":32}},"602":{"start":{"line":603,"column":0},"end":{"line":603,"column":32}},"603":{"start":{"line":604,"column":0},"end":{"line":604,"column":11}},"604":{"start":{"line":605,"column":0},"end":{"line":605,"column":7}},"605":{"start":{"line":606,"column":0},"end":{"line":606,"column":5}},"607":{"start":{"line":608,"column":0},"end":{"line":608,"column":16}},"608":{"start":{"line":609,"column":0},"end":{"line":609,"column":16}},"609":{"start":{"line":610,"column":0},"end":{"line":610,"column":20}},"610":{"start":{"line":611,"column":0},"end":{"line":611,"column":70}},"611":{"start":{"line":612,"column":0},"end":{"line":612,"column":5}},"614":{"start":{"line":615,"column":0},"end":{"line":615,"column":27}},"615":{"start":{"line":616,"column":0},"end":{"line":616,"column":66}},"616":{"start":{"line":617,"column":0},"end":{"line":617,"column":39}},"617":{"start":{"line":618,"column":0},"end":{"line":618,"column":26}},"618":{"start":{"line":619,"column":0},"end":{"line":619,"column":6}},"620":{"start":{"line":621,"column":0},"end":{"line":621,"column":33}},"621":{"start":{"line":622,"column":0},"end":{"line":622,"column":45}},"622":{"start":{"line":623,"column":0},"end":{"line":623,"column":12}},"623":{"start":{"line":624,"column":0},"end":{"line":624,"column":11}},"624":{"start":{"line":625,"column":0},"end":{"line":625,"column":7}},"625":{"start":{"line":626,"column":0},"end":{"line":626,"column":19}},"626":{"start":{"line":627,"column":0},"end":{"line":627,"column":16}},"627":{"start":{"line":628,"column":0},"end":{"line":628,"column":3}},"628":{"start":{"line":629,"column":0},"end":{"line":629,"column":2}},"631":{"start":{"line":632,"column":0},"end":{"line":632,"column":33}},"632":{"start":{"line":633,"column":0},"end":{"line":633,"column":49}},"633":{"start":{"line":634,"column":0},"end":{"line":634,"column":16}},"634":{"start":{"line":635,"column":0},"end":{"line":635,"column":21}},"635":{"start":{"line":636,"column":0},"end":{"line":636,"column":6}},"636":{"start":{"line":637,"column":0},"end":{"line":637,"column":29}},"637":{"start":{"line":638,"column":0},"end":{"line":638,"column":15}},"638":{"start":{"line":639,"column":0},"end":{"line":639,"column":66}},"639":{"start":{"line":640,"column":0},"end":{"line":640,"column":3}},"641":{"start":{"line":642,"column":0},"end":{"line":642,"column":7}},"642":{"start":{"line":643,"column":0},"end":{"line":643,"column":58}},"643":{"start":{"line":644,"column":0},"end":{"line":644,"column":16}},"644":{"start":{"line":645,"column":0},"end":{"line":645,"column":35}},"645":{"start":{"line":646,"column":0},"end":{"line":646,"column":16}},"646":{"start":{"line":647,"column":0},"end":{"line":647,"column":110}},"647":{"start":{"line":648,"column":0},"end":{"line":648,"column":9}},"648":{"start":{"line":649,"column":0},"end":{"line":649,"column":5}},"650":{"start":{"line":651,"column":0},"end":{"line":651,"column":31}},"653":{"start":{"line":654,"column":0},"end":{"line":654,"column":64}},"656":{"start":{"line":657,"column":0},"end":{"line":657,"column":41}},"657":{"start":{"line":658,"column":0},"end":{"line":658,"column":45}},"658":{"start":{"line":659,"column":0},"end":{"line":659,"column":22}},"659":{"start":{"line":660,"column":0},"end":{"line":660,"column":21}},"660":{"start":{"line":661,"column":0},"end":{"line":661,"column":76}},"661":{"start":{"line":662,"column":0},"end":{"line":662,"column":28}},"662":{"start":{"line":663,"column":0},"end":{"line":663,"column":28}},"663":{"start":{"line":664,"column":0},"end":{"line":664,"column":7}},"665":{"start":{"line":666,"column":0},"end":{"line":666,"column":18}},"666":{"start":{"line":667,"column":0},"end":{"line":667,"column":70}},"667":{"start":{"line":668,"column":0},"end":{"line":668,"column":5}},"670":{"start":{"line":671,"column":0},"end":{"line":671,"column":33}},"671":{"start":{"line":672,"column":0},"end":{"line":672,"column":62}},"672":{"start":{"line":673,"column":0},"end":{"line":673,"column":22}},"673":{"start":{"line":674,"column":0},"end":{"line":674,"column":24}},"674":{"start":{"line":675,"column":0},"end":{"line":675,"column":7}},"675":{"start":{"line":676,"column":0},"end":{"line":676,"column":19}},"676":{"start":{"line":677,"column":0},"end":{"line":677,"column":36}},"677":{"start":{"line":678,"column":0},"end":{"line":678,"column":75}},"678":{"start":{"line":679,"column":0},"end":{"line":679,"column":5}},"679":{"start":{"line":680,"column":0},"end":{"line":680,"column":16}},"680":{"start":{"line":681,"column":0},"end":{"line":681,"column":3}},"681":{"start":{"line":682,"column":0},"end":{"line":682,"column":2}}},"s":{"0":1,"1":1,"2":1,"3":1,"4":1,"5":1,"6":1,"7":1,"8":1,"9":1,"10":1,"12":1,"13":1,"66":1,"67":1,"68":1,"69":1,"70":1,"72":1,"75":1,"78":1,"79":0,"80":0,"81":0,"82":0,"83":0,"84":0,"85":0,"86":0,"87":0,"88":0,"89":0,"91":0,"92":0,"93":0,"94":0,"95":0,"97":0,"98":0,"103":0,"105":0,"106":0,"108":0,"109":0,"110":0,"111":0,"112":0,"113":0,"114":0,"115":0,"117":0,"118":0,"119":0,"120":0,"121":0,"122":0,"123":0,"124":0,"126":0,"127":0,"128":0,"131":1,"132":2,"133":2,"134":2,"135":2,"136":2,"137":2,"140":2,"141":1,"142":1,"144":2,"145":0,"146":0,"148":2,"149":0,"150":0,"152":1,"153":1,"154":1,"155":1,"156":2,"157":0,"158":0,"159":0,"160":0,"161":0,"164":1,"165":0,"166":0,"167":0,"170":0,"171":0,"172":0,"173":0,"176":0,"179":0,"180":0,"181":0,"182":0,"183":0,"184":0,"185":2,"186":2,"187":2,"188":2,"189":2,"190":2,"191":2,"192":2,"193":2,"194":2,"195":2,"196":2,"198":0,"199":0,"200":0,"203":0,"204":0,"207":0,"208":0,"209":0,"210":0,"211":0,"212":0,"213":0,"215":0,"216":0,"217":0,"220":0,"223":0,"224":0,"225":0,"226":0,"227":0,"228":0,"229":2,"230":2,"231":2,"232":2,"233":2,"234":2,"235":2,"236":2,"237":2,"238":0,"239":0,"240":2,"241":2,"244":1,"245":0,"246":0,"247":0,"248":0,"249":0,"250":0,"251":0,"252":0,"253":0,"254":0,"255":0,"256":0,"257":0,"258":0,"259":0,"260":0,"261":0,"263":0,"264":0,"265":0,"266":0,"268":0,"269":0,"270":0,"271":0,"272":0,"273":0,"274":0,"275":0,"277":0,"278":0,"279":0,"280":0,"281":0,"282":0,"283":0,"285":0,"286":0,"287":0,"288":0,"289":0,"290":0,"291":0,"292":0,"293":0,"294":0,"295":0,"296":0,"298":0,"299":0,"300":0,"301":0,"302":0,"305":1,"306":0,"307":0,"308":0,"309":0,"311":0,"312":0,"313":0,"314":0,"315":0,"316":0,"317":0,"319":0,"320":0,"321":0,"322":0,"323":0,"324":0,"325":0,"326":0,"328":0,"329":0,"330":0,"331":0,"332":0,"333":0,"334":0,"336":0,"337":0,"338":0,"339":0,"341":0,"342":0,"343":0,"344":0,"345":0,"346":0,"347":0,"348":0,"349":0,"350":0,"351":0,"352":0,"353":0,"354":0,"355":0,"356":0,"357":0,"358":0,"359":0,"362":1,"363":1,"364":1,"365":1,"366":1,"367":1,"368":1,"370":1,"371":1,"372":1,"373":1,"374":1,"375":1,"376":1,"377":1,"378":1,"380":0,"381":0,"382":0,"384":0,"385":0,"386":0,"387":0,"389":0,"390":0,"391":0,"392":0,"393":0,"394":0,"395":0,"396":0,"398":0,"399":0,"400":0,"401":0,"402":0,"403":0,"404":0,"405":0,"407":0,"409":0,"410":0,"411":0,"412":0,"413":0,"414":0,"415":1,"416":1,"417":1,"418":1,"419":1,"420":1,"421":1,"422":1,"423":1,"424":0,"425":0,"426":1,"427":1,"430":1,"431":0,"432":0,"433":0,"434":0,"435":0,"436":0,"438":0,"439":0,"440":0,"441":0,"442":0,"445":0,"448":0,"449":0,"450":0,"451":0,"453":0,"454":0,"455":0,"456":0,"457":0,"458":0,"461":0,"462":0,"463":0,"464":0,"465":0,"467":0,"468":0,"469":0,"470":0,"471":0,"472":0,"473":0,"474":0,"475":0,"476":0,"477":0,"478":0,"481":1,"482":0,"483":0,"484":0,"485":0,"486":0,"488":0,"489":0,"490":0,"491":0,"494":0,"495":0,"496":0,"497":0,"499":0,"502":0,"503":0,"504":0,"505":0,"506":0,"508":0,"509":0,"510":0,"511":0,"513":0,"514":0,"515":0,"516":0,"517":0,"518":0,"519":0,"521":0,"523":0,"524":0,"525":0,"526":0,"527":0,"528":0,"529":0,"530":0,"531":0,"532":0,"533":0,"534":0,"535":0,"536":0,"537":0,"538":0,"539":0,"540":0,"541":0,"542":0,"543":0,"544":0,"545":0,"546":0,"547":0,"548":0,"549":0,"550":0,"551":0,"554":0,"555":0,"561":0,"562":0,"564":0,"566":0,"567":0,"568":0,"569":0,"570":0,"572":0,"573":0,"575":0,"577":0,"578":0,"579":0,"580":0,"581":0,"582":0,"583":0,"584":0,"585":0,"586":0,"587":0,"588":0,"589":0,"590":0,"591":0,"592":0,"593":0,"594":0,"595":0,"596":0,"597":0,"598":0,"599":0,"600":0,"601":0,"602":0,"603":0,"604":0,"605":0,"607":0,"608":0,"609":0,"610":0,"611":0,"614":0,"615":0,"616":0,"617":0,"618":0,"620":0,"621":0,"622":0,"623":0,"624":0,"625":0,"626":0,"627":0,"628":0,"631":1,"632":0,"633":0,"634":0,"635":0,"636":0,"637":0,"638":0,"639":0,"641":0,"642":0,"643":0,"644":0,"645":0,"646":0,"647":0,"648":0,"650":0,"653":0,"656":0,"657":0,"658":0,"659":0,"660":0,"661":0,"662":0,"663":0,"665":0,"666":0,"667":0,"670":0,"671":0,"672":0,"673":0,"674":0,"675":0,"676":0,"677":0,"678":0,"679":0,"680":0,"681":0},"branchMap":{"0":{"type":"branch","line":132,"loc":{"start":{"line":132,"column":27},"end":{"line":242,"column":2}},"locations":[{"start":{"line":132,"column":27},"end":{"line":242,"column":2}}]},"1":{"type":"branch","line":141,"loc":{"start":{"line":141,"column":9},"end":{"line":141,"column":29}},"locations":[{"start":{"line":141,"column":9},"end":{"line":141,"column":29}}]},"2":{"type":"branch","line":141,"loc":{"start":{"line":141,"column":29},"end":{"line":145,"column":29}},"locations":[{"start":{"line":141,"column":29},"end":{"line":145,"column":29}}]},"3":{"type":"branch","line":145,"loc":{"start":{"line":145,"column":29},"end":{"line":147,"column":5}},"locations":[{"start":{"line":145,"column":29},"end":{"line":147,"column":5}}]},"4":{"type":"branch","line":147,"loc":{"start":{"line":147,"column":4},"end":{"line":149,"column":55}},"locations":[{"start":{"line":147,"column":4},"end":{"line":149,"column":55}}]},"5":{"type":"branch","line":149,"loc":{"start":{"line":149,"column":55},"end":{"line":151,"column":5}},"locations":[{"start":{"line":149,"column":55},"end":{"line":151,"column":5}}]},"6":{"type":"branch","line":151,"loc":{"start":{"line":151,"column":4},"end":{"line":156,"column":67}},"locations":[{"start":{"line":151,"column":4},"end":{"line":156,"column":67}}]},"7":{"type":"branch","line":157,"loc":{"start":{"line":157,"column":6},"end":{"line":162,"column":5}},"locations":[{"start":{"line":157,"column":6},"end":{"line":162,"column":5}}]},"8":{"type":"branch","line":162,"loc":{"start":{"line":162,"column":4},"end":{"line":165,"column":64}},"locations":[{"start":{"line":162,"column":4},"end":{"line":165,"column":64}}]},"9":{"type":"branch","line":165,"loc":{"start":{"line":165,"column":62},"end":{"line":185,"column":44}},"locations":[{"start":{"line":165,"column":62},"end":{"line":185,"column":44}}]},"10":{"type":"branch","line":197,"loc":{"start":{"line":197,"column":5},"end":{"line":230,"column":11}},"locations":[{"start":{"line":197,"column":5},"end":{"line":230,"column":11}}]},"11":{"type":"branch","line":235,"loc":{"start":{"line":235,"column":27},"end":{"line":235,"column":57}},"locations":[{"start":{"line":235,"column":27},"end":{"line":235,"column":57}}]},"12":{"type":"branch","line":235,"loc":{"start":{"line":235,"column":44},"end":{"line":235,"column":61}},"locations":[{"start":{"line":235,"column":44},"end":{"line":235,"column":61}}]},"13":{"type":"branch","line":238,"loc":{"start":{"line":238,"column":4},"end":{"line":240,"column":5}},"locations":[{"start":{"line":238,"column":4},"end":{"line":240,"column":5}}]},"14":{"type":"branch","line":363,"loc":{"start":{"line":363,"column":21},"end":{"line":428,"column":2}},"locations":[{"start":{"line":363,"column":21},"end":{"line":428,"column":2}}]},"15":{"type":"branch","line":379,"loc":{"start":{"line":379,"column":5},"end":{"line":416,"column":11}},"locations":[{"start":{"line":379,"column":5},"end":{"line":416,"column":11}}]},"16":{"type":"branch","line":421,"loc":{"start":{"line":421,"column":27},"end":{"line":421,"column":57}},"locations":[{"start":{"line":421,"column":27},"end":{"line":421,"column":57}}]},"17":{"type":"branch","line":424,"loc":{"start":{"line":424,"column":4},"end":{"line":426,"column":5}},"locations":[{"start":{"line":424,"column":4},"end":{"line":426,"column":5}}]}},"b":{"0":[2],"1":[1],"2":[1],"3":[0],"4":[1],"5":[0],"6":[1],"7":[0],"8":[1],"9":[0],"10":[0],"11":[1],"12":[1],"13":[0],"14":[1],"15":[0],"16":[0],"17":[0]},"fnMap":{"0":{"name":"generateOTP","decl":{"start":{"line":76,"column":20},"end":{"line":76,"column":59}},"loc":{"start":{"line":76,"column":20},"end":{"line":76,"column":59}},"line":76},"1":{"name":"sendOTP","decl":{"start":{"line":79,"column":16},"end":{"line":129,"column":2}},"loc":{"start":{"line":79,"column":16},"end":{"line":129,"column":2}},"line":79},"2":{"name":"verifyPhone","decl":{"start":{"line":132,"column":27},"end":{"line":242,"column":2}},"loc":{"start":{"line":132,"column":27},"end":{"line":242,"column":2}},"line":132},"3":{"name":"register_user","decl":{"start":{"line":245,"column":29},"end":{"line":303,"column":2}},"loc":{"start":{"line":245,"column":29},"end":{"line":303,"column":2}},"line":245},"4":{"name":"verifyOtp","decl":{"start":{"line":306,"column":25},"end":{"line":360,"column":2}},"loc":{"start":{"line":306,"column":25},"end":{"line":360,"column":2}},"line":306},"5":{"name":"logIn","decl":{"start":{"line":363,"column":21},"end":{"line":428,"column":2}},"loc":{"start":{"line":363,"column":21},"end":{"line":428,"column":2}},"line":363},"6":{"name":"refreshToken","decl":{"start":{"line":431,"column":28},"end":{"line":479,"column":2}},"loc":{"start":{"line":431,"column":28},"end":{"line":479,"column":2}},"line":431},"7":{"name":"authHandler","decl":{"start":{"line":482,"column":27},"end":{"line":629,"column":2}},"loc":{"start":{"line":482,"column":27},"end":{"line":629,"column":2}},"line":482},"8":{"name":"sendNewOTP","decl":{"start":{"line":632,"column":26},"end":{"line":682,"column":2}},"loc":{"start":{"line":632,"column":26},"end":{"line":682,"column":2}},"line":632}},"f":{"0":0,"1":0,"2":2,"3":0,"4":0,"5":1,"6":0,"7":0,"8":0}} +,"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/src/controllers/multerStorage.ts": {"path":"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/src/controllers/multerStorage.ts","all":true,"statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":68}},"1":{"start":{"line":2,"column":0},"end":{"line":2,"column":24}},"3":{"start":{"line":4,"column":0},"end":{"line":4,"column":46}},"10":{"start":{"line":11,"column":0},"end":{"line":11,"column":51}},"11":{"start":{"line":12,"column":0},"end":{"line":12,"column":37}},"12":{"start":{"line":13,"column":0},"end":{"line":13,"column":36}},"13":{"start":{"line":14,"column":0},"end":{"line":14,"column":4}},"14":{"start":{"line":15,"column":0},"end":{"line":15,"column":33}},"15":{"start":{"line":16,"column":0},"end":{"line":16,"column":76}},"16":{"start":{"line":17,"column":0},"end":{"line":17,"column":7}},"17":{"start":{"line":18,"column":0},"end":{"line":18,"column":11}},"18":{"start":{"line":19,"column":0},"end":{"line":19,"column":76}},"19":{"start":{"line":20,"column":0},"end":{"line":20,"column":6}},"20":{"start":{"line":21,"column":0},"end":{"line":21,"column":4}},"21":{"start":{"line":22,"column":0},"end":{"line":22,"column":3}},"24":{"start":{"line":25,"column":0},"end":{"line":25,"column":31}},"25":{"start":{"line":26,"column":0},"end":{"line":26,"column":10}},"26":{"start":{"line":27,"column":0},"end":{"line":27,"column":11}},"27":{"start":{"line":28,"column":0},"end":{"line":28,"column":51}},"28":{"start":{"line":29,"column":0},"end":{"line":29,"column":4}},"29":{"start":{"line":30,"column":0},"end":{"line":30,"column":35}},"31":{"start":{"line":32,"column":0},"end":{"line":32,"column":45}},"32":{"start":{"line":33,"column":0},"end":{"line":33,"column":52}},"33":{"start":{"line":34,"column":0},"end":{"line":34,"column":13}},"34":{"start":{"line":35,"column":0},"end":{"line":35,"column":5}},"35":{"start":{"line":36,"column":0},"end":{"line":36,"column":19}},"36":{"start":{"line":37,"column":0},"end":{"line":37,"column":4}},"37":{"start":{"line":38,"column":0},"end":{"line":38,"column":3}},"40":{"start":{"line":41,"column":0},"end":{"line":41,"column":49}},"41":{"start":{"line":42,"column":0},"end":{"line":42,"column":46}},"43":{"start":{"line":44,"column":0},"end":{"line":44,"column":10}},"44":{"start":{"line":45,"column":0},"end":{"line":45,"column":23}},"45":{"start":{"line":46,"column":0},"end":{"line":46,"column":24}},"46":{"start":{"line":47,"column":0},"end":{"line":47,"column":23}},"47":{"start":{"line":48,"column":0},"end":{"line":48,"column":14}},"49":{"start":{"line":50,"column":0},"end":{"line":50,"column":48}},"50":{"start":{"line":51,"column":0},"end":{"line":51,"column":49}},"51":{"start":{"line":52,"column":0},"end":{"line":52,"column":55}},"56":{"start":{"line":57,"column":0},"end":{"line":57,"column":62}},"57":{"start":{"line":58,"column":0},"end":{"line":58,"column":39}},"58":{"start":{"line":59,"column":0},"end":{"line":59,"column":45}},"59":{"start":{"line":60,"column":0},"end":{"line":60,"column":78}},"60":{"start":{"line":61,"column":0},"end":{"line":61,"column":17}},"61":{"start":{"line":62,"column":0},"end":{"line":62,"column":9}},"62":{"start":{"line":63,"column":0},"end":{"line":63,"column":45}},"63":{"start":{"line":64,"column":0},"end":{"line":64,"column":15}},"64":{"start":{"line":65,"column":0},"end":{"line":65,"column":7}},"65":{"start":{"line":66,"column":0},"end":{"line":66,"column":33}},"66":{"start":{"line":67,"column":0},"end":{"line":67,"column":45}},"67":{"start":{"line":68,"column":0},"end":{"line":68,"column":15}},"68":{"start":{"line":69,"column":0},"end":{"line":69,"column":7}},"69":{"start":{"line":70,"column":0},"end":{"line":70,"column":13}},"70":{"start":{"line":71,"column":0},"end":{"line":71,"column":7}},"71":{"start":{"line":72,"column":0},"end":{"line":72,"column":4}},"72":{"start":{"line":73,"column":0},"end":{"line":73,"column":2}},"74":{"start":{"line":75,"column":0},"end":{"line":75,"column":32}}},"s":{"0":0,"1":0,"3":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0,"31":0,"32":0,"33":0,"34":0,"35":0,"36":0,"37":0,"40":0,"41":0,"43":0,"44":0,"45":0,"46":0,"47":0,"49":0,"50":0,"51":0,"56":0,"57":0,"58":0,"59":0,"60":0,"61":0,"62":0,"63":0,"64":0,"65":0,"66":0,"67":0,"68":0,"69":0,"70":0,"71":0,"72":0,"74":0},"branchMap":{"0":{"type":"branch","line":1,"loc":{"start":{"line":1,"column":2188},"end":{"line":75,"column":32}},"locations":[{"start":{"line":1,"column":2188},"end":{"line":75,"column":32}}]}},"b":{"0":[0]},"fnMap":{"0":{"name":"(empty-report)","decl":{"start":{"line":1,"column":2188},"end":{"line":75,"column":32}},"loc":{"start":{"line":1,"column":2188},"end":{"line":75,"column":32}},"line":1}},"f":{"0":0}} +,"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/src/controllers/notification.controller.ts": {"path":"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/src/controllers/notification.controller.ts","all":false,"statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":49}},"1":{"start":{"line":2,"column":0},"end":{"line":2,"column":34}},"3":{"start":{"line":4,"column":0},"end":{"line":4,"column":46}},"28":{"start":{"line":29,"column":0},"end":{"line":29,"column":38}},"29":{"start":{"line":30,"column":0},"end":{"line":30,"column":35}},"30":{"start":{"line":31,"column":0},"end":{"line":31,"column":16}},"31":{"start":{"line":32,"column":0},"end":{"line":32,"column":21}},"32":{"start":{"line":33,"column":0},"end":{"line":33,"column":21}},"33":{"start":{"line":34,"column":0},"end":{"line":34,"column":32}},"35":{"start":{"line":36,"column":0},"end":{"line":36,"column":7}},"36":{"start":{"line":37,"column":0},"end":{"line":37,"column":65}},"37":{"start":{"line":38,"column":0},"end":{"line":38,"column":24}},"38":{"start":{"line":39,"column":0},"end":{"line":39,"column":37}},"39":{"start":{"line":40,"column":0},"end":{"line":40,"column":16}},"40":{"start":{"line":41,"column":0},"end":{"line":41,"column":20}},"41":{"start":{"line":42,"column":0},"end":{"line":42,"column":79}},"42":{"start":{"line":43,"column":0},"end":{"line":43,"column":8}},"43":{"start":{"line":44,"column":0},"end":{"line":44,"column":7}},"45":{"start":{"line":46,"column":0},"end":{"line":46,"column":26}},"46":{"start":{"line":47,"column":0},"end":{"line":47,"column":38}},"47":{"start":{"line":48,"column":0},"end":{"line":48,"column":7}},"48":{"start":{"line":49,"column":0},"end":{"line":49,"column":19}},"49":{"start":{"line":50,"column":0},"end":{"line":50,"column":16}},"50":{"start":{"line":51,"column":0},"end":{"line":51,"column":3}},"51":{"start":{"line":52,"column":0},"end":{"line":52,"column":2}},"54":{"start":{"line":55,"column":0},"end":{"line":55,"column":41}},"55":{"start":{"line":56,"column":0},"end":{"line":56,"column":71}},"56":{"start":{"line":57,"column":0},"end":{"line":57,"column":16}},"57":{"start":{"line":58,"column":0},"end":{"line":58,"column":21}},"58":{"start":{"line":59,"column":0},"end":{"line":59,"column":21}},"59":{"start":{"line":60,"column":0},"end":{"line":60,"column":32}},"60":{"start":{"line":61,"column":0},"end":{"line":61,"column":38}},"62":{"start":{"line":63,"column":0},"end":{"line":63,"column":7}},"63":{"start":{"line":64,"column":0},"end":{"line":64,"column":29}},"64":{"start":{"line":65,"column":0},"end":{"line":65,"column":64}},"65":{"start":{"line":66,"column":0},"end":{"line":66,"column":5}},"67":{"start":{"line":68,"column":0},"end":{"line":68,"column":52}},"68":{"start":{"line":69,"column":0},"end":{"line":69,"column":13}},"69":{"start":{"line":70,"column":0},"end":{"line":70,"column":14}},"70":{"start":{"line":71,"column":0},"end":{"line":71,"column":12}},"71":{"start":{"line":72,"column":0},"end":{"line":72,"column":20}},"72":{"start":{"line":73,"column":0},"end":{"line":73,"column":28}},"73":{"start":{"line":74,"column":0},"end":{"line":74,"column":28}},"74":{"start":{"line":75,"column":0},"end":{"line":75,"column":7}},"76":{"start":{"line":77,"column":0},"end":{"line":77,"column":24}},"77":{"start":{"line":78,"column":0},"end":{"line":78,"column":63}},"78":{"start":{"line":79,"column":0},"end":{"line":79,"column":5}},"80":{"start":{"line":81,"column":0},"end":{"line":81,"column":26}},"81":{"start":{"line":82,"column":0},"end":{"line":82,"column":27}},"82":{"start":{"line":83,"column":0},"end":{"line":83,"column":7}},"83":{"start":{"line":84,"column":0},"end":{"line":84,"column":19}},"84":{"start":{"line":85,"column":0},"end":{"line":85,"column":36}},"85":{"start":{"line":86,"column":0},"end":{"line":86,"column":68}},"86":{"start":{"line":87,"column":0},"end":{"line":87,"column":13}},"87":{"start":{"line":88,"column":0},"end":{"line":88,"column":5}},"88":{"start":{"line":89,"column":0},"end":{"line":89,"column":16}},"89":{"start":{"line":90,"column":0},"end":{"line":90,"column":3}},"90":{"start":{"line":91,"column":0},"end":{"line":91,"column":2}},"93":{"start":{"line":94,"column":0},"end":{"line":94,"column":33}},"94":{"start":{"line":95,"column":0},"end":{"line":95,"column":31}},"95":{"start":{"line":96,"column":0},"end":{"line":96,"column":16}},"96":{"start":{"line":97,"column":0},"end":{"line":97,"column":21}},"97":{"start":{"line":98,"column":0},"end":{"line":98,"column":21}},"98":{"start":{"line":99,"column":0},"end":{"line":99,"column":44}},"100":{"start":{"line":101,"column":0},"end":{"line":101,"column":7}},"101":{"start":{"line":102,"column":0},"end":{"line":102,"column":53}},"102":{"start":{"line":103,"column":0},"end":{"line":103,"column":46}},"103":{"start":{"line":104,"column":0},"end":{"line":104,"column":40}},"104":{"start":{"line":105,"column":0},"end":{"line":105,"column":6}},"106":{"start":{"line":107,"column":0},"end":{"line":107,"column":29}},"107":{"start":{"line":108,"column":0},"end":{"line":108,"column":56}},"108":{"start":{"line":109,"column":0},"end":{"line":109,"column":5}},"110":{"start":{"line":111,"column":0},"end":{"line":111,"column":26}},"111":{"start":{"line":112,"column":0},"end":{"line":112,"column":45}},"112":{"start":{"line":113,"column":0},"end":{"line":113,"column":7}},"113":{"start":{"line":114,"column":0},"end":{"line":114,"column":19}},"114":{"start":{"line":115,"column":0},"end":{"line":115,"column":36}},"115":{"start":{"line":116,"column":0},"end":{"line":116,"column":68}},"116":{"start":{"line":117,"column":0},"end":{"line":117,"column":13}},"117":{"start":{"line":118,"column":0},"end":{"line":118,"column":5}},"118":{"start":{"line":119,"column":0},"end":{"line":119,"column":16}},"119":{"start":{"line":120,"column":0},"end":{"line":120,"column":3}},"120":{"start":{"line":121,"column":0},"end":{"line":121,"column":2}},"123":{"start":{"line":124,"column":0},"end":{"line":124,"column":43}},"124":{"start":{"line":125,"column":0},"end":{"line":125,"column":35}},"125":{"start":{"line":126,"column":0},"end":{"line":126,"column":16}},"126":{"start":{"line":127,"column":0},"end":{"line":127,"column":21}},"127":{"start":{"line":128,"column":0},"end":{"line":128,"column":21}},"128":{"start":{"line":129,"column":0},"end":{"line":129,"column":32}},"130":{"start":{"line":131,"column":0},"end":{"line":131,"column":7}},"131":{"start":{"line":132,"column":0},"end":{"line":132,"column":67}},"132":{"start":{"line":133,"column":0},"end":{"line":133,"column":20}},"133":{"start":{"line":134,"column":0},"end":{"line":134,"column":48}},"134":{"start":{"line":135,"column":0},"end":{"line":135,"column":5}},"136":{"start":{"line":137,"column":0},"end":{"line":137,"column":34}},"137":{"start":{"line":138,"column":0},"end":{"line":138,"column":67}},"138":{"start":{"line":139,"column":0},"end":{"line":139,"column":5}},"140":{"start":{"line":141,"column":0},"end":{"line":141,"column":34}},"141":{"start":{"line":142,"column":0},"end":{"line":142,"column":33}},"142":{"start":{"line":143,"column":0},"end":{"line":143,"column":23}},"143":{"start":{"line":144,"column":0},"end":{"line":144,"column":33}},"144":{"start":{"line":145,"column":0},"end":{"line":145,"column":34}},"145":{"start":{"line":146,"column":0},"end":{"line":146,"column":23}},"146":{"start":{"line":147,"column":0},"end":{"line":147,"column":6}},"148":{"start":{"line":149,"column":0},"end":{"line":149,"column":74}},"149":{"start":{"line":150,"column":0},"end":{"line":150,"column":21}},"150":{"start":{"line":151,"column":0},"end":{"line":151,"column":16}},"151":{"start":{"line":152,"column":0},"end":{"line":152,"column":35}},"152":{"start":{"line":153,"column":0},"end":{"line":153,"column":43}},"153":{"start":{"line":154,"column":0},"end":{"line":154,"column":8}},"154":{"start":{"line":155,"column":0},"end":{"line":155,"column":36}},"155":{"start":{"line":156,"column":0},"end":{"line":156,"column":7}},"157":{"start":{"line":158,"column":0},"end":{"line":158,"column":23}},"158":{"start":{"line":159,"column":0},"end":{"line":159,"column":25}},"159":{"start":{"line":160,"column":0},"end":{"line":160,"column":62}},"160":{"start":{"line":161,"column":0},"end":{"line":161,"column":24}},"161":{"start":{"line":162,"column":0},"end":{"line":162,"column":8}},"162":{"start":{"line":163,"column":0},"end":{"line":163,"column":5}},"164":{"start":{"line":165,"column":0},"end":{"line":165,"column":59}},"167":{"start":{"line":168,"column":0},"end":{"line":168,"column":71}},"168":{"start":{"line":169,"column":0},"end":{"line":169,"column":16}},"169":{"start":{"line":170,"column":0},"end":{"line":170,"column":75}},"170":{"start":{"line":171,"column":0},"end":{"line":171,"column":5}},"172":{"start":{"line":173,"column":0},"end":{"line":173,"column":26}},"173":{"start":{"line":174,"column":0},"end":{"line":174,"column":48}},"174":{"start":{"line":175,"column":0},"end":{"line":175,"column":13}},"175":{"start":{"line":176,"column":0},"end":{"line":176,"column":7}},"176":{"start":{"line":177,"column":0},"end":{"line":177,"column":19}},"177":{"start":{"line":178,"column":0},"end":{"line":178,"column":36}},"178":{"start":{"line":179,"column":0},"end":{"line":179,"column":68}},"179":{"start":{"line":180,"column":0},"end":{"line":180,"column":13}},"180":{"start":{"line":181,"column":0},"end":{"line":181,"column":5}},"181":{"start":{"line":182,"column":0},"end":{"line":182,"column":16}},"182":{"start":{"line":183,"column":0},"end":{"line":183,"column":3}},"183":{"start":{"line":184,"column":0},"end":{"line":184,"column":2}}},"s":{"0":1,"1":1,"3":1,"28":1,"29":0,"30":0,"31":0,"32":0,"33":0,"35":0,"36":0,"37":0,"38":0,"39":0,"40":0,"41":0,"42":0,"43":0,"45":0,"46":0,"47":0,"48":0,"49":0,"50":0,"51":0,"54":1,"55":0,"56":0,"57":0,"58":0,"59":0,"60":0,"62":0,"63":0,"64":0,"65":0,"67":0,"68":0,"69":0,"70":0,"71":0,"72":0,"73":0,"74":0,"76":0,"77":0,"78":0,"80":0,"81":0,"82":0,"83":0,"84":0,"85":0,"86":0,"87":0,"88":0,"89":0,"90":0,"93":1,"94":0,"95":0,"96":0,"97":0,"98":0,"100":0,"101":0,"102":0,"103":0,"104":0,"106":0,"107":0,"108":0,"110":0,"111":0,"112":0,"113":0,"114":0,"115":0,"116":0,"117":0,"118":0,"119":0,"120":0,"123":1,"124":0,"125":0,"126":0,"127":0,"128":0,"130":0,"131":0,"132":0,"133":0,"134":0,"136":0,"137":0,"138":0,"140":0,"141":0,"142":0,"143":0,"144":0,"145":0,"146":0,"148":0,"149":0,"150":0,"151":0,"152":0,"153":0,"154":0,"155":0,"157":0,"158":0,"159":0,"160":0,"161":0,"162":0,"164":0,"167":0,"168":0,"169":0,"170":0,"172":0,"173":0,"174":0,"175":0,"176":0,"177":0,"178":0,"179":0,"180":0,"181":0,"182":0,"183":0},"branchMap":{},"b":{},"fnMap":{"0":{"name":"getNotification","decl":{"start":{"line":29,"column":31},"end":{"line":52,"column":2}},"loc":{"start":{"line":29,"column":31},"end":{"line":52,"column":2}},"line":29},"1":{"name":"createNotification","decl":{"start":{"line":55,"column":34},"end":{"line":91,"column":2}},"loc":{"start":{"line":55,"column":34},"end":{"line":91,"column":2}},"line":55},"2":{"name":"markAsRead","decl":{"start":{"line":94,"column":26},"end":{"line":121,"column":2}},"loc":{"start":{"line":94,"column":26},"end":{"line":121,"column":2}},"line":94},"3":{"name":"testExpoNotification","decl":{"start":{"line":124,"column":36},"end":{"line":184,"column":2}},"loc":{"start":{"line":124,"column":36},"end":{"line":184,"column":2}},"line":124}},"f":{"0":0,"1":0,"2":0,"3":0}} +,"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/src/controllers/order.controller.ts": {"path":"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/src/controllers/order.controller.ts","all":false,"statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":34}},"1":{"start":{"line":2,"column":0},"end":{"line":2,"column":48}},"2":{"start":{"line":3,"column":0},"end":{"line":3,"column":40}},"3":{"start":{"line":4,"column":0},"end":{"line":4,"column":36}},"4":{"start":{"line":5,"column":0},"end":{"line":5,"column":48}},"5":{"start":{"line":6,"column":0},"end":{"line":6,"column":34}},"6":{"start":{"line":7,"column":0},"end":{"line":7,"column":45}},"7":{"start":{"line":8,"column":0},"end":{"line":8,"column":46}},"8":{"start":{"line":9,"column":0},"end":{"line":9,"column":25}},"9":{"start":{"line":10,"column":0},"end":{"line":10,"column":73}},"10":{"start":{"line":11,"column":0},"end":{"line":11,"column":28}},"14":{"start":{"line":15,"column":0},"end":{"line":15,"column":46}},"60":{"start":{"line":61,"column":0},"end":{"line":61,"column":19}},"61":{"start":{"line":62,"column":0},"end":{"line":62,"column":48}},"62":{"start":{"line":63,"column":0},"end":{"line":63,"column":42}},"63":{"start":{"line":64,"column":0},"end":{"line":64,"column":48}},"64":{"start":{"line":65,"column":0},"end":{"line":65,"column":3}},"67":{"start":{"line":68,"column":0},"end":{"line":68,"column":35}},"68":{"start":{"line":69,"column":0},"end":{"line":69,"column":36}},"69":{"start":{"line":70,"column":0},"end":{"line":70,"column":16}},"70":{"start":{"line":71,"column":0},"end":{"line":71,"column":21}},"71":{"start":{"line":72,"column":0},"end":{"line":72,"column":7}},"72":{"start":{"line":73,"column":0},"end":{"line":73,"column":43}},"73":{"start":{"line":74,"column":0},"end":{"line":74,"column":40}},"74":{"start":{"line":75,"column":0},"end":{"line":75,"column":16}},"75":{"start":{"line":76,"column":0},"end":{"line":76,"column":9}},"76":{"start":{"line":77,"column":0},"end":{"line":77,"column":22}},"77":{"start":{"line":78,"column":0},"end":{"line":78,"column":22}},"78":{"start":{"line":79,"column":0},"end":{"line":79,"column":23}},"79":{"start":{"line":80,"column":0},"end":{"line":80,"column":17}},"80":{"start":{"line":81,"column":0},"end":{"line":81,"column":24}},"81":{"start":{"line":82,"column":0},"end":{"line":82,"column":23}},"82":{"start":{"line":83,"column":0},"end":{"line":83,"column":22}},"83":{"start":{"line":84,"column":0},"end":{"line":84,"column":27}},"84":{"start":{"line":85,"column":0},"end":{"line":85,"column":12}},"85":{"start":{"line":86,"column":0},"end":{"line":86,"column":10}},"86":{"start":{"line":87,"column":0},"end":{"line":87,"column":9}},"87":{"start":{"line":88,"column":0},"end":{"line":88,"column":22}},"88":{"start":{"line":89,"column":0},"end":{"line":89,"column":23}},"89":{"start":{"line":90,"column":0},"end":{"line":90,"column":23}},"90":{"start":{"line":91,"column":0},"end":{"line":91,"column":17}},"91":{"start":{"line":92,"column":0},"end":{"line":92,"column":24}},"92":{"start":{"line":93,"column":0},"end":{"line":93,"column":23}},"93":{"start":{"line":94,"column":0},"end":{"line":94,"column":22}},"94":{"start":{"line":95,"column":0},"end":{"line":95,"column":27}},"95":{"start":{"line":96,"column":0},"end":{"line":96,"column":12}},"96":{"start":{"line":97,"column":0},"end":{"line":97,"column":10}},"97":{"start":{"line":98,"column":0},"end":{"line":98,"column":9}},"98":{"start":{"line":99,"column":0},"end":{"line":99,"column":25}},"99":{"start":{"line":100,"column":0},"end":{"line":100,"column":20}},"100":{"start":{"line":101,"column":0},"end":{"line":101,"column":13}},"101":{"start":{"line":102,"column":0},"end":{"line":102,"column":26}},"102":{"start":{"line":103,"column":0},"end":{"line":103,"column":27}},"103":{"start":{"line":104,"column":0},"end":{"line":104,"column":21}},"104":{"start":{"line":105,"column":0},"end":{"line":105,"column":28}},"105":{"start":{"line":106,"column":0},"end":{"line":106,"column":27}},"106":{"start":{"line":107,"column":0},"end":{"line":107,"column":26}},"107":{"start":{"line":108,"column":0},"end":{"line":108,"column":31}},"108":{"start":{"line":109,"column":0},"end":{"line":109,"column":16}},"109":{"start":{"line":110,"column":0},"end":{"line":110,"column":14}},"110":{"start":{"line":111,"column":0},"end":{"line":111,"column":13}},"111":{"start":{"line":112,"column":0},"end":{"line":112,"column":33}},"112":{"start":{"line":113,"column":0},"end":{"line":113,"column":54}},"113":{"start":{"line":114,"column":0},"end":{"line":114,"column":30}},"114":{"start":{"line":115,"column":0},"end":{"line":115,"column":14}},"115":{"start":{"line":116,"column":0},"end":{"line":116,"column":12}},"116":{"start":{"line":117,"column":0},"end":{"line":117,"column":10}},"117":{"start":{"line":118,"column":0},"end":{"line":118,"column":8}},"118":{"start":{"line":119,"column":0},"end":{"line":119,"column":7}},"120":{"start":{"line":121,"column":0},"end":{"line":121,"column":21}},"121":{"start":{"line":122,"column":0},"end":{"line":122,"column":49}},"122":{"start":{"line":123,"column":0},"end":{"line":123,"column":5}},"124":{"start":{"line":125,"column":0},"end":{"line":125,"column":26}},"125":{"start":{"line":126,"column":0},"end":{"line":126,"column":24}},"126":{"start":{"line":127,"column":0},"end":{"line":127,"column":23}},"127":{"start":{"line":128,"column":0},"end":{"line":128,"column":7}},"128":{"start":{"line":129,"column":0},"end":{"line":129,"column":19}},"129":{"start":{"line":130,"column":0},"end":{"line":130,"column":36}},"130":{"start":{"line":131,"column":0},"end":{"line":131,"column":68}},"131":{"start":{"line":132,"column":0},"end":{"line":132,"column":13}},"132":{"start":{"line":133,"column":0},"end":{"line":133,"column":5}},"133":{"start":{"line":134,"column":0},"end":{"line":134,"column":26}},"134":{"start":{"line":135,"column":0},"end":{"line":135,"column":76}},"135":{"start":{"line":136,"column":0},"end":{"line":136,"column":7}},"136":{"start":{"line":137,"column":0},"end":{"line":137,"column":3}},"137":{"start":{"line":138,"column":0},"end":{"line":138,"column":2}},"140":{"start":{"line":141,"column":0},"end":{"line":141,"column":37}},"141":{"start":{"line":142,"column":0},"end":{"line":142,"column":80}},"142":{"start":{"line":143,"column":0},"end":{"line":143,"column":16}},"143":{"start":{"line":144,"column":0},"end":{"line":144,"column":21}},"144":{"start":{"line":145,"column":0},"end":{"line":145,"column":33}},"145":{"start":{"line":146,"column":0},"end":{"line":146,"column":36}},"147":{"start":{"line":148,"column":0},"end":{"line":148,"column":41}},"148":{"start":{"line":149,"column":0},"end":{"line":149,"column":12}},"149":{"start":{"line":150,"column":0},"end":{"line":150,"column":4}},"151":{"start":{"line":152,"column":0},"end":{"line":152,"column":49}},"152":{"start":{"line":153,"column":0},"end":{"line":153,"column":37}},"153":{"start":{"line":154,"column":0},"end":{"line":154,"column":3}},"155":{"start":{"line":156,"column":0},"end":{"line":156,"column":7}},"156":{"start":{"line":157,"column":0},"end":{"line":157,"column":53}},"157":{"start":{"line":158,"column":0},"end":{"line":158,"column":41}},"158":{"start":{"line":159,"column":0},"end":{"line":159,"column":16}},"159":{"start":{"line":160,"column":0},"end":{"line":160,"column":9}},"160":{"start":{"line":161,"column":0},"end":{"line":161,"column":22}},"161":{"start":{"line":162,"column":0},"end":{"line":162,"column":23}},"162":{"start":{"line":163,"column":0},"end":{"line":163,"column":23}},"163":{"start":{"line":164,"column":0},"end":{"line":164,"column":17}},"164":{"start":{"line":165,"column":0},"end":{"line":165,"column":24}},"165":{"start":{"line":166,"column":0},"end":{"line":166,"column":23}},"166":{"start":{"line":167,"column":0},"end":{"line":167,"column":22}},"167":{"start":{"line":168,"column":0},"end":{"line":168,"column":27}},"168":{"start":{"line":169,"column":0},"end":{"line":169,"column":12}},"169":{"start":{"line":170,"column":0},"end":{"line":170,"column":10}},"170":{"start":{"line":171,"column":0},"end":{"line":171,"column":9}},"171":{"start":{"line":172,"column":0},"end":{"line":172,"column":25}},"172":{"start":{"line":173,"column":0},"end":{"line":173,"column":20}},"173":{"start":{"line":174,"column":0},"end":{"line":174,"column":13}},"174":{"start":{"line":175,"column":0},"end":{"line":175,"column":26}},"175":{"start":{"line":176,"column":0},"end":{"line":176,"column":27}},"176":{"start":{"line":177,"column":0},"end":{"line":177,"column":21}},"177":{"start":{"line":178,"column":0},"end":{"line":178,"column":28}},"178":{"start":{"line":179,"column":0},"end":{"line":179,"column":27}},"179":{"start":{"line":180,"column":0},"end":{"line":180,"column":26}},"180":{"start":{"line":181,"column":0},"end":{"line":181,"column":31}},"181":{"start":{"line":182,"column":0},"end":{"line":182,"column":16}},"182":{"start":{"line":183,"column":0},"end":{"line":183,"column":14}},"183":{"start":{"line":184,"column":0},"end":{"line":184,"column":13}},"184":{"start":{"line":185,"column":0},"end":{"line":185,"column":33}},"185":{"start":{"line":186,"column":0},"end":{"line":186,"column":54}},"186":{"start":{"line":187,"column":0},"end":{"line":187,"column":30}},"187":{"start":{"line":188,"column":0},"end":{"line":188,"column":14}},"188":{"start":{"line":189,"column":0},"end":{"line":189,"column":12}},"189":{"start":{"line":190,"column":0},"end":{"line":190,"column":10}},"190":{"start":{"line":191,"column":0},"end":{"line":191,"column":8}},"191":{"start":{"line":192,"column":0},"end":{"line":192,"column":7}},"193":{"start":{"line":194,"column":0},"end":{"line":194,"column":26}},"194":{"start":{"line":195,"column":0},"end":{"line":195,"column":24}},"195":{"start":{"line":196,"column":0},"end":{"line":196,"column":30}},"196":{"start":{"line":197,"column":0},"end":{"line":197,"column":7}},"197":{"start":{"line":198,"column":0},"end":{"line":198,"column":19}},"198":{"start":{"line":199,"column":0},"end":{"line":199,"column":26}},"199":{"start":{"line":200,"column":0},"end":{"line":200,"column":76}},"200":{"start":{"line":201,"column":0},"end":{"line":201,"column":7}},"201":{"start":{"line":202,"column":0},"end":{"line":202,"column":3}},"202":{"start":{"line":203,"column":0},"end":{"line":203,"column":2}},"205":{"start":{"line":206,"column":0},"end":{"line":206,"column":38}},"206":{"start":{"line":207,"column":0},"end":{"line":207,"column":15}},"212":{"start":{"line":213,"column":0},"end":{"line":213,"column":16}},"213":{"start":{"line":214,"column":0},"end":{"line":214,"column":21}},"214":{"start":{"line":215,"column":0},"end":{"line":215,"column":34}},"215":{"start":{"line":216,"column":0},"end":{"line":216,"column":49}},"217":{"start":{"line":218,"column":0},"end":{"line":218,"column":41}},"218":{"start":{"line":219,"column":0},"end":{"line":219,"column":13}},"219":{"start":{"line":220,"column":0},"end":{"line":220,"column":4}},"221":{"start":{"line":222,"column":0},"end":{"line":222,"column":49}},"223":{"start":{"line":224,"column":0},"end":{"line":224,"column":49}},"224":{"start":{"line":225,"column":0},"end":{"line":225,"column":37}},"225":{"start":{"line":226,"column":0},"end":{"line":226,"column":3}},"226":{"start":{"line":227,"column":0},"end":{"line":227,"column":49}},"227":{"start":{"line":228,"column":0},"end":{"line":228,"column":35}},"228":{"start":{"line":229,"column":0},"end":{"line":229,"column":36}},"229":{"start":{"line":230,"column":0},"end":{"line":230,"column":6}},"230":{"start":{"line":231,"column":0},"end":{"line":231,"column":3}},"232":{"start":{"line":233,"column":0},"end":{"line":233,"column":7}},"233":{"start":{"line":234,"column":0},"end":{"line":234,"column":54}},"234":{"start":{"line":235,"column":0},"end":{"line":235,"column":41}},"235":{"start":{"line":236,"column":0},"end":{"line":236,"column":16}},"236":{"start":{"line":237,"column":0},"end":{"line":237,"column":9}},"237":{"start":{"line":238,"column":0},"end":{"line":238,"column":22}},"238":{"start":{"line":239,"column":0},"end":{"line":239,"column":22}},"239":{"start":{"line":240,"column":0},"end":{"line":240,"column":23}},"240":{"start":{"line":241,"column":0},"end":{"line":241,"column":17}},"241":{"start":{"line":242,"column":0},"end":{"line":242,"column":24}},"242":{"start":{"line":243,"column":0},"end":{"line":243,"column":23}},"243":{"start":{"line":244,"column":0},"end":{"line":244,"column":22}},"244":{"start":{"line":245,"column":0},"end":{"line":245,"column":27}},"245":{"start":{"line":246,"column":0},"end":{"line":246,"column":12}},"246":{"start":{"line":247,"column":0},"end":{"line":247,"column":10}},"247":{"start":{"line":248,"column":0},"end":{"line":248,"column":9}},"248":{"start":{"line":249,"column":0},"end":{"line":249,"column":25}},"249":{"start":{"line":250,"column":0},"end":{"line":250,"column":49}},"250":{"start":{"line":251,"column":0},"end":{"line":251,"column":20}},"251":{"start":{"line":252,"column":0},"end":{"line":252,"column":13}},"252":{"start":{"line":253,"column":0},"end":{"line":253,"column":26}},"253":{"start":{"line":254,"column":0},"end":{"line":254,"column":27}},"254":{"start":{"line":255,"column":0},"end":{"line":255,"column":21}},"255":{"start":{"line":256,"column":0},"end":{"line":256,"column":28}},"256":{"start":{"line":257,"column":0},"end":{"line":257,"column":27}},"257":{"start":{"line":258,"column":0},"end":{"line":258,"column":26}},"258":{"start":{"line":259,"column":0},"end":{"line":259,"column":31}},"259":{"start":{"line":260,"column":0},"end":{"line":260,"column":16}},"260":{"start":{"line":261,"column":0},"end":{"line":261,"column":14}},"261":{"start":{"line":262,"column":0},"end":{"line":262,"column":13}},"262":{"start":{"line":263,"column":0},"end":{"line":263,"column":33}},"263":{"start":{"line":264,"column":0},"end":{"line":264,"column":54}},"264":{"start":{"line":265,"column":0},"end":{"line":265,"column":30}},"265":{"start":{"line":266,"column":0},"end":{"line":266,"column":14}},"266":{"start":{"line":267,"column":0},"end":{"line":267,"column":12}},"267":{"start":{"line":268,"column":0},"end":{"line":268,"column":10}},"268":{"start":{"line":269,"column":0},"end":{"line":269,"column":8}},"269":{"start":{"line":270,"column":0},"end":{"line":270,"column":7}},"271":{"start":{"line":272,"column":0},"end":{"line":272,"column":26}},"272":{"start":{"line":273,"column":0},"end":{"line":273,"column":24}},"273":{"start":{"line":274,"column":0},"end":{"line":274,"column":31}},"274":{"start":{"line":275,"column":0},"end":{"line":275,"column":7}},"275":{"start":{"line":276,"column":0},"end":{"line":276,"column":19}},"276":{"start":{"line":277,"column":0},"end":{"line":277,"column":26}},"277":{"start":{"line":278,"column":0},"end":{"line":278,"column":76}},"278":{"start":{"line":279,"column":0},"end":{"line":279,"column":7}},"279":{"start":{"line":280,"column":0},"end":{"line":280,"column":3}},"280":{"start":{"line":281,"column":0},"end":{"line":281,"column":2}},"283":{"start":{"line":284,"column":0},"end":{"line":284,"column":34}},"284":{"start":{"line":285,"column":0},"end":{"line":285,"column":67}},"285":{"start":{"line":286,"column":0},"end":{"line":286,"column":16}},"286":{"start":{"line":287,"column":0},"end":{"line":287,"column":21}},"287":{"start":{"line":288,"column":0},"end":{"line":288,"column":52}},"289":{"start":{"line":290,"column":0},"end":{"line":290,"column":7}},"290":{"start":{"line":291,"column":0},"end":{"line":291,"column":63}},"293":{"start":{"line":294,"column":0},"end":{"line":294,"column":58}},"294":{"start":{"line":295,"column":0},"end":{"line":295,"column":57}},"295":{"start":{"line":296,"column":0},"end":{"line":296,"column":5}},"298":{"start":{"line":299,"column":0},"end":{"line":299,"column":67}},"299":{"start":{"line":300,"column":0},"end":{"line":300,"column":18}},"300":{"start":{"line":301,"column":0},"end":{"line":301,"column":73}},"301":{"start":{"line":302,"column":0},"end":{"line":302,"column":5}},"303":{"start":{"line":304,"column":0},"end":{"line":304,"column":37}},"304":{"start":{"line":305,"column":0},"end":{"line":305,"column":7}},"305":{"start":{"line":306,"column":0},"end":{"line":306,"column":15}},"306":{"start":{"line":307,"column":0},"end":{"line":307,"column":20}},"307":{"start":{"line":308,"column":0},"end":{"line":308,"column":15}},"308":{"start":{"line":309,"column":0},"end":{"line":309,"column":28}},"309":{"start":{"line":310,"column":0},"end":{"line":310,"column":37}},"310":{"start":{"line":311,"column":0},"end":{"line":311,"column":104}},"311":{"start":{"line":312,"column":0},"end":{"line":312,"column":26}},"312":{"start":{"line":313,"column":0},"end":{"line":313,"column":26}},"313":{"start":{"line":314,"column":0},"end":{"line":314,"column":30}},"314":{"start":{"line":315,"column":0},"end":{"line":315,"column":30}},"315":{"start":{"line":316,"column":0},"end":{"line":316,"column":8}},"316":{"start":{"line":317,"column":0},"end":{"line":317,"column":22}},"317":{"start":{"line":318,"column":0},"end":{"line":318,"column":6}},"319":{"start":{"line":320,"column":0},"end":{"line":320,"column":29}},"320":{"start":{"line":321,"column":0},"end":{"line":321,"column":7}},"321":{"start":{"line":322,"column":0},"end":{"line":322,"column":29}},"322":{"start":{"line":323,"column":0},"end":{"line":323,"column":26}},"323":{"start":{"line":324,"column":0},"end":{"line":324,"column":26}},"324":{"start":{"line":325,"column":0},"end":{"line":325,"column":30}},"325":{"start":{"line":326,"column":0},"end":{"line":326,"column":30}},"326":{"start":{"line":327,"column":0},"end":{"line":327,"column":8}},"327":{"start":{"line":328,"column":0},"end":{"line":328,"column":22}},"328":{"start":{"line":329,"column":0},"end":{"line":329,"column":6}},"330":{"start":{"line":331,"column":0},"end":{"line":331,"column":31}},"332":{"start":{"line":333,"column":0},"end":{"line":333,"column":26}},"333":{"start":{"line":334,"column":0},"end":{"line":334,"column":14}},"334":{"start":{"line":335,"column":0},"end":{"line":335,"column":108}},"335":{"start":{"line":336,"column":0},"end":{"line":336,"column":26}},"336":{"start":{"line":337,"column":0},"end":{"line":337,"column":7}},"337":{"start":{"line":338,"column":0},"end":{"line":338,"column":19}},"338":{"start":{"line":339,"column":0},"end":{"line":339,"column":33}},"339":{"start":{"line":340,"column":0},"end":{"line":340,"column":36}},"340":{"start":{"line":341,"column":0},"end":{"line":341,"column":68}},"341":{"start":{"line":342,"column":0},"end":{"line":342,"column":13}},"342":{"start":{"line":343,"column":0},"end":{"line":343,"column":5}},"343":{"start":{"line":344,"column":0},"end":{"line":344,"column":26}},"344":{"start":{"line":345,"column":0},"end":{"line":345,"column":76}},"345":{"start":{"line":346,"column":0},"end":{"line":346,"column":7}},"346":{"start":{"line":347,"column":0},"end":{"line":347,"column":3}},"347":{"start":{"line":348,"column":0},"end":{"line":348,"column":2}},"350":{"start":{"line":351,"column":0},"end":{"line":351,"column":34}},"351":{"start":{"line":352,"column":0},"end":{"line":352,"column":65}},"352":{"start":{"line":353,"column":0},"end":{"line":353,"column":16}},"353":{"start":{"line":354,"column":0},"end":{"line":354,"column":21}},"354":{"start":{"line":355,"column":0},"end":{"line":355,"column":7}},"355":{"start":{"line":356,"column":0},"end":{"line":356,"column":47}},"356":{"start":{"line":357,"column":0},"end":{"line":357,"column":45}},"357":{"start":{"line":358,"column":0},"end":{"line":358,"column":7}},"359":{"start":{"line":360,"column":0},"end":{"line":360,"column":19}},"360":{"start":{"line":361,"column":0},"end":{"line":361,"column":70}},"361":{"start":{"line":362,"column":0},"end":{"line":362,"column":5}},"363":{"start":{"line":364,"column":0},"end":{"line":364,"column":41}},"364":{"start":{"line":365,"column":0},"end":{"line":365,"column":25}},"365":{"start":{"line":366,"column":0},"end":{"line":366,"column":70}},"366":{"start":{"line":367,"column":0},"end":{"line":367,"column":12}},"367":{"start":{"line":368,"column":0},"end":{"line":368,"column":8}},"368":{"start":{"line":369,"column":0},"end":{"line":369,"column":5}},"370":{"start":{"line":371,"column":0},"end":{"line":371,"column":23}},"371":{"start":{"line":372,"column":0},"end":{"line":372,"column":7}},"372":{"start":{"line":373,"column":0},"end":{"line":373,"column":29}},"373":{"start":{"line":374,"column":0},"end":{"line":374,"column":30}},"374":{"start":{"line":375,"column":0},"end":{"line":375,"column":8}},"375":{"start":{"line":376,"column":0},"end":{"line":376,"column":7}},"376":{"start":{"line":377,"column":0},"end":{"line":377,"column":42}},"377":{"start":{"line":378,"column":0},"end":{"line":378,"column":8}},"378":{"start":{"line":379,"column":0},"end":{"line":379,"column":6}},"380":{"start":{"line":381,"column":0},"end":{"line":381,"column":63}},"381":{"start":{"line":382,"column":0},"end":{"line":382,"column":21}},"382":{"start":{"line":383,"column":0},"end":{"line":383,"column":49}},"383":{"start":{"line":384,"column":0},"end":{"line":384,"column":5}},"385":{"start":{"line":386,"column":0},"end":{"line":386,"column":55}},"386":{"start":{"line":387,"column":0},"end":{"line":387,"column":40}},"387":{"start":{"line":388,"column":0},"end":{"line":388,"column":37}},"388":{"start":{"line":389,"column":0},"end":{"line":389,"column":7}},"391":{"start":{"line":392,"column":0},"end":{"line":392,"column":36}},"392":{"start":{"line":393,"column":0},"end":{"line":393,"column":56}},"393":{"start":{"line":394,"column":0},"end":{"line":394,"column":33}},"394":{"start":{"line":395,"column":0},"end":{"line":395,"column":76}},"395":{"start":{"line":396,"column":0},"end":{"line":396,"column":8}},"397":{"start":{"line":398,"column":0},"end":{"line":398,"column":52}},"398":{"start":{"line":399,"column":0},"end":{"line":399,"column":41}},"399":{"start":{"line":400,"column":0},"end":{"line":400,"column":42}},"400":{"start":{"line":401,"column":0},"end":{"line":401,"column":8}},"402":{"start":{"line":403,"column":0},"end":{"line":403,"column":54}},"403":{"start":{"line":404,"column":0},"end":{"line":404,"column":33}},"404":{"start":{"line":405,"column":0},"end":{"line":405,"column":20}},"405":{"start":{"line":406,"column":0},"end":{"line":406,"column":8}},"407":{"start":{"line":408,"column":0},"end":{"line":408,"column":41}},"408":{"start":{"line":409,"column":0},"end":{"line":409,"column":33}},"409":{"start":{"line":410,"column":0},"end":{"line":410,"column":33}},"410":{"start":{"line":411,"column":0},"end":{"line":411,"column":29}},"411":{"start":{"line":412,"column":0},"end":{"line":412,"column":30}},"412":{"start":{"line":413,"column":0},"end":{"line":413,"column":10}},"413":{"start":{"line":414,"column":0},"end":{"line":414,"column":7}},"414":{"start":{"line":415,"column":0},"end":{"line":415,"column":5}},"416":{"start":{"line":417,"column":0},"end":{"line":417,"column":35}},"417":{"start":{"line":418,"column":0},"end":{"line":418,"column":56}},"418":{"start":{"line":419,"column":0},"end":{"line":419,"column":34}},"419":{"start":{"line":420,"column":0},"end":{"line":420,"column":59}},"420":{"start":{"line":421,"column":0},"end":{"line":421,"column":8}},"422":{"start":{"line":423,"column":0},"end":{"line":423,"column":52}},"423":{"start":{"line":424,"column":0},"end":{"line":424,"column":41}},"424":{"start":{"line":425,"column":0},"end":{"line":425,"column":42}},"425":{"start":{"line":426,"column":0},"end":{"line":426,"column":8}},"427":{"start":{"line":428,"column":0},"end":{"line":428,"column":54}},"428":{"start":{"line":429,"column":0},"end":{"line":429,"column":32}},"429":{"start":{"line":430,"column":0},"end":{"line":430,"column":20}},"430":{"start":{"line":431,"column":0},"end":{"line":431,"column":8}},"432":{"start":{"line":433,"column":0},"end":{"line":433,"column":41}},"433":{"start":{"line":434,"column":0},"end":{"line":434,"column":33}},"434":{"start":{"line":435,"column":0},"end":{"line":435,"column":33}},"435":{"start":{"line":436,"column":0},"end":{"line":436,"column":26}},"436":{"start":{"line":437,"column":0},"end":{"line":437,"column":30}},"437":{"start":{"line":438,"column":0},"end":{"line":438,"column":10}},"438":{"start":{"line":439,"column":0},"end":{"line":439,"column":7}},"439":{"start":{"line":440,"column":0},"end":{"line":440,"column":5}},"441":{"start":{"line":442,"column":0},"end":{"line":442,"column":71}},"442":{"start":{"line":443,"column":0},"end":{"line":443,"column":19}},"443":{"start":{"line":444,"column":0},"end":{"line":444,"column":36}},"444":{"start":{"line":445,"column":0},"end":{"line":445,"column":68}},"445":{"start":{"line":446,"column":0},"end":{"line":446,"column":13}},"446":{"start":{"line":447,"column":0},"end":{"line":447,"column":5}},"447":{"start":{"line":448,"column":0},"end":{"line":448,"column":26}},"448":{"start":{"line":449,"column":0},"end":{"line":449,"column":76}},"449":{"start":{"line":450,"column":0},"end":{"line":450,"column":7}},"450":{"start":{"line":451,"column":0},"end":{"line":451,"column":3}},"451":{"start":{"line":452,"column":0},"end":{"line":452,"column":2}},"454":{"start":{"line":455,"column":0},"end":{"line":455,"column":37}},"455":{"start":{"line":456,"column":0},"end":{"line":456,"column":36}},"456":{"start":{"line":457,"column":0},"end":{"line":457,"column":16}},"457":{"start":{"line":458,"column":0},"end":{"line":458,"column":21}},"458":{"start":{"line":459,"column":0},"end":{"line":459,"column":7}},"459":{"start":{"line":460,"column":0},"end":{"line":460,"column":51}},"460":{"start":{"line":461,"column":0},"end":{"line":461,"column":45}},"461":{"start":{"line":462,"column":0},"end":{"line":462,"column":7}},"463":{"start":{"line":464,"column":0},"end":{"line":464,"column":23}},"464":{"start":{"line":465,"column":0},"end":{"line":465,"column":55}},"465":{"start":{"line":466,"column":0},"end":{"line":466,"column":5}},"467":{"start":{"line":468,"column":0},"end":{"line":468,"column":26}},"468":{"start":{"line":469,"column":0},"end":{"line":469,"column":37}},"469":{"start":{"line":470,"column":0},"end":{"line":470,"column":27}},"470":{"start":{"line":471,"column":0},"end":{"line":471,"column":7}},"471":{"start":{"line":472,"column":0},"end":{"line":472,"column":19}},"472":{"start":{"line":473,"column":0},"end":{"line":473,"column":36}},"473":{"start":{"line":474,"column":0},"end":{"line":474,"column":68}},"474":{"start":{"line":475,"column":0},"end":{"line":475,"column":13}},"475":{"start":{"line":476,"column":0},"end":{"line":476,"column":5}},"476":{"start":{"line":477,"column":0},"end":{"line":477,"column":26}},"477":{"start":{"line":478,"column":0},"end":{"line":478,"column":76}},"478":{"start":{"line":479,"column":0},"end":{"line":479,"column":7}},"479":{"start":{"line":480,"column":0},"end":{"line":480,"column":3}},"480":{"start":{"line":481,"column":0},"end":{"line":481,"column":2}},"483":{"start":{"line":484,"column":0},"end":{"line":484,"column":44}},"484":{"start":{"line":485,"column":0},"end":{"line":485,"column":62}},"485":{"start":{"line":486,"column":0},"end":{"line":486,"column":16}},"486":{"start":{"line":487,"column":0},"end":{"line":487,"column":21}},"487":{"start":{"line":488,"column":0},"end":{"line":488,"column":21}},"488":{"start":{"line":489,"column":0},"end":{"line":489,"column":7}},"489":{"start":{"line":490,"column":0},"end":{"line":490,"column":35}},"490":{"start":{"line":491,"column":0},"end":{"line":491,"column":38}},"492":{"start":{"line":493,"column":0},"end":{"line":493,"column":27}},"493":{"start":{"line":494,"column":0},"end":{"line":494,"column":62}},"494":{"start":{"line":495,"column":0},"end":{"line":495,"column":5}},"496":{"start":{"line":497,"column":0},"end":{"line":497,"column":38}},"497":{"start":{"line":498,"column":0},"end":{"line":498,"column":31}},"498":{"start":{"line":499,"column":0},"end":{"line":499,"column":13}},"499":{"start":{"line":500,"column":0},"end":{"line":500,"column":6}},"501":{"start":{"line":502,"column":0},"end":{"line":502,"column":19}},"502":{"start":{"line":503,"column":0},"end":{"line":503,"column":69}},"503":{"start":{"line":504,"column":0},"end":{"line":504,"column":22}},"504":{"start":{"line":505,"column":0},"end":{"line":505,"column":9}},"505":{"start":{"line":506,"column":0},"end":{"line":506,"column":33}},"506":{"start":{"line":507,"column":0},"end":{"line":507,"column":10}},"507":{"start":{"line":508,"column":0},"end":{"line":508,"column":8}},"509":{"start":{"line":510,"column":0},"end":{"line":510,"column":58}},"510":{"start":{"line":511,"column":0},"end":{"line":511,"column":35}},"511":{"start":{"line":512,"column":0},"end":{"line":512,"column":5}},"513":{"start":{"line":514,"column":0},"end":{"line":514,"column":46}},"514":{"start":{"line":515,"column":0},"end":{"line":515,"column":7}},"515":{"start":{"line":516,"column":0},"end":{"line":516,"column":29}},"516":{"start":{"line":517,"column":0},"end":{"line":517,"column":25}},"517":{"start":{"line":518,"column":0},"end":{"line":518,"column":33}},"518":{"start":{"line":519,"column":0},"end":{"line":519,"column":27}},"519":{"start":{"line":520,"column":0},"end":{"line":520,"column":30}},"520":{"start":{"line":521,"column":0},"end":{"line":521,"column":8}},"521":{"start":{"line":522,"column":0},"end":{"line":522,"column":7}},"522":{"start":{"line":523,"column":0},"end":{"line":523,"column":31}},"523":{"start":{"line":524,"column":0},"end":{"line":524,"column":8}},"524":{"start":{"line":525,"column":0},"end":{"line":525,"column":6}},"526":{"start":{"line":527,"column":0},"end":{"line":527,"column":29}},"527":{"start":{"line":528,"column":0},"end":{"line":528,"column":49}},"528":{"start":{"line":529,"column":0},"end":{"line":529,"column":5}},"530":{"start":{"line":531,"column":0},"end":{"line":531,"column":26}},"531":{"start":{"line":532,"column":0},"end":{"line":532,"column":55}},"532":{"start":{"line":533,"column":0},"end":{"line":533,"column":7}},"533":{"start":{"line":534,"column":0},"end":{"line":534,"column":19}},"534":{"start":{"line":535,"column":0},"end":{"line":535,"column":36}},"535":{"start":{"line":536,"column":0},"end":{"line":536,"column":68}},"536":{"start":{"line":537,"column":0},"end":{"line":537,"column":13}},"537":{"start":{"line":538,"column":0},"end":{"line":538,"column":5}},"538":{"start":{"line":539,"column":0},"end":{"line":539,"column":16}},"539":{"start":{"line":540,"column":0},"end":{"line":540,"column":3}},"540":{"start":{"line":541,"column":0},"end":{"line":541,"column":2}}},"s":{"0":1,"1":1,"2":1,"3":1,"4":1,"5":1,"6":1,"7":1,"8":1,"9":1,"10":1,"14":1,"60":1,"61":1,"62":1,"63":1,"64":1,"67":1,"68":0,"69":0,"70":0,"71":0,"72":0,"73":0,"74":0,"75":0,"76":0,"77":0,"78":0,"79":0,"80":0,"81":0,"82":0,"83":0,"84":0,"85":0,"86":0,"87":0,"88":0,"89":0,"90":0,"91":0,"92":0,"93":0,"94":0,"95":0,"96":0,"97":0,"98":0,"99":0,"100":0,"101":0,"102":0,"103":0,"104":0,"105":0,"106":0,"107":0,"108":0,"109":0,"110":0,"111":0,"112":0,"113":0,"114":0,"115":0,"116":0,"117":0,"118":0,"120":0,"121":0,"122":0,"124":0,"125":0,"126":0,"127":0,"128":0,"129":0,"130":0,"131":0,"132":0,"133":0,"134":0,"135":0,"136":0,"137":0,"140":1,"141":0,"142":0,"143":0,"144":0,"145":0,"147":0,"148":0,"149":0,"151":0,"152":0,"153":0,"155":0,"156":0,"157":0,"158":0,"159":0,"160":0,"161":0,"162":0,"163":0,"164":0,"165":0,"166":0,"167":0,"168":0,"169":0,"170":0,"171":0,"172":0,"173":0,"174":0,"175":0,"176":0,"177":0,"178":0,"179":0,"180":0,"181":0,"182":0,"183":0,"184":0,"185":0,"186":0,"187":0,"188":0,"189":0,"190":0,"191":0,"193":0,"194":0,"195":0,"196":0,"197":0,"198":0,"199":0,"200":0,"201":0,"202":0,"205":1,"206":0,"212":0,"213":0,"214":0,"215":0,"217":0,"218":0,"219":0,"221":0,"223":0,"224":0,"225":0,"226":0,"227":0,"228":0,"229":0,"230":0,"232":0,"233":0,"234":0,"235":0,"236":0,"237":0,"238":0,"239":0,"240":0,"241":0,"242":0,"243":0,"244":0,"245":0,"246":0,"247":0,"248":0,"249":0,"250":0,"251":0,"252":0,"253":0,"254":0,"255":0,"256":0,"257":0,"258":0,"259":0,"260":0,"261":0,"262":0,"263":0,"264":0,"265":0,"266":0,"267":0,"268":0,"269":0,"271":0,"272":0,"273":0,"274":0,"275":0,"276":0,"277":0,"278":0,"279":0,"280":0,"283":1,"284":0,"285":0,"286":0,"287":0,"289":0,"290":0,"293":0,"294":0,"295":0,"298":0,"299":0,"300":0,"301":0,"303":0,"304":0,"305":0,"306":0,"307":0,"308":0,"309":0,"310":0,"311":0,"312":0,"313":0,"314":0,"315":0,"316":0,"317":0,"319":0,"320":0,"321":0,"322":0,"323":0,"324":0,"325":0,"326":0,"327":0,"328":0,"330":0,"332":0,"333":0,"334":0,"335":0,"336":0,"337":0,"338":0,"339":0,"340":0,"341":0,"342":0,"343":0,"344":0,"345":0,"346":0,"347":0,"350":1,"351":0,"352":0,"353":0,"354":0,"355":0,"356":0,"357":0,"359":0,"360":0,"361":0,"363":0,"364":0,"365":0,"366":0,"367":0,"368":0,"370":0,"371":0,"372":0,"373":0,"374":0,"375":0,"376":0,"377":0,"378":0,"380":0,"381":0,"382":0,"383":0,"385":0,"386":0,"387":0,"388":0,"391":0,"392":0,"393":0,"394":0,"395":0,"397":0,"398":0,"399":0,"400":0,"402":0,"403":0,"404":0,"405":0,"407":0,"408":0,"409":0,"410":0,"411":0,"412":0,"413":0,"414":0,"416":0,"417":0,"418":0,"419":0,"420":0,"422":0,"423":0,"424":0,"425":0,"427":0,"428":0,"429":0,"430":0,"432":0,"433":0,"434":0,"435":0,"436":0,"437":0,"438":0,"439":0,"441":0,"442":0,"443":0,"444":0,"445":0,"446":0,"447":0,"448":0,"449":0,"450":0,"451":0,"454":1,"455":0,"456":0,"457":0,"458":0,"459":0,"460":0,"461":0,"463":0,"464":0,"465":0,"467":0,"468":0,"469":0,"470":0,"471":0,"472":0,"473":0,"474":0,"475":0,"476":0,"477":0,"478":0,"479":0,"480":0,"483":1,"484":0,"485":0,"486":0,"487":0,"488":0,"489":0,"490":0,"492":0,"493":0,"494":0,"496":0,"497":0,"498":0,"499":0,"501":0,"502":0,"503":0,"504":0,"505":0,"506":0,"507":0,"509":0,"510":0,"511":0,"513":0,"514":0,"515":0,"516":0,"517":0,"518":0,"519":0,"520":0,"521":0,"522":0,"523":0,"524":0,"526":0,"527":0,"528":0,"530":0,"531":0,"532":0,"533":0,"534":0,"535":0,"536":0,"537":0,"538":0,"539":0,"540":0},"branchMap":{},"b":{},"fnMap":{"0":{"name":"getOrderById","decl":{"start":{"line":68,"column":28},"end":{"line":138,"column":2}},"loc":{"start":{"line":68,"column":28},"end":{"line":138,"column":2}},"line":68},"1":{"name":"getBuyerOrders","decl":{"start":{"line":141,"column":30},"end":{"line":203,"column":2}},"loc":{"start":{"line":141,"column":30},"end":{"line":203,"column":2}},"line":141},"2":{"name":"getSellerOrders","decl":{"start":{"line":206,"column":31},"end":{"line":281,"column":2}},"loc":{"start":{"line":206,"column":31},"end":{"line":281,"column":2}},"line":206},"3":{"name":"createOrder","decl":{"start":{"line":284,"column":27},"end":{"line":348,"column":2}},"loc":{"start":{"line":284,"column":27},"end":{"line":348,"column":2}},"line":284},"4":{"name":"updateOrder","decl":{"start":{"line":351,"column":27},"end":{"line":452,"column":2}},"loc":{"start":{"line":351,"column":27},"end":{"line":452,"column":2}},"line":351},"5":{"name":"getTransaction","decl":{"start":{"line":455,"column":30},"end":{"line":481,"column":2}},"loc":{"start":{"line":455,"column":30},"end":{"line":481,"column":2}},"line":455},"6":{"name":"updateDispatchDetails","decl":{"start":{"line":484,"column":37},"end":{"line":541,"column":2}},"loc":{"start":{"line":484,"column":37},"end":{"line":541,"column":2}},"line":484}},"f":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0}} +,"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/src/controllers/payment.collection.controller.ts": {"path":"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/src/controllers/payment.collection.controller.ts","all":true,"statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":34}},"1":{"start":{"line":2,"column":0},"end":{"line":2,"column":36}},"2":{"start":{"line":3,"column":0},"end":{"line":3,"column":48}},"3":{"start":{"line":4,"column":0},"end":{"line":4,"column":42}},"5":{"start":{"line":6,"column":0},"end":{"line":6,"column":34}},"6":{"start":{"line":7,"column":0},"end":{"line":7,"column":73}},"7":{"start":{"line":8,"column":0},"end":{"line":8,"column":28}},"10":{"start":{"line":11,"column":0},"end":{"line":11,"column":46}},"47":{"start":{"line":48,"column":0},"end":{"line":48,"column":51}},"48":{"start":{"line":49,"column":0},"end":{"line":49,"column":57}},"49":{"start":{"line":50,"column":0},"end":{"line":50,"column":59}},"50":{"start":{"line":51,"column":0},"end":{"line":51,"column":47}},"52":{"start":{"line":53,"column":0},"end":{"line":53,"column":78}},"53":{"start":{"line":54,"column":0},"end":{"line":54,"column":65}},"54":{"start":{"line":55,"column":0},"end":{"line":55,"column":1}},"57":{"start":{"line":58,"column":0},"end":{"line":58,"column":57}},"58":{"start":{"line":59,"column":0},"end":{"line":59,"column":7}},"59":{"start":{"line":60,"column":0},"end":{"line":60,"column":33}},"60":{"start":{"line":61,"column":0},"end":{"line":61,"column":35}},"61":{"start":{"line":62,"column":0},"end":{"line":62,"column":7}},"63":{"start":{"line":64,"column":0},"end":{"line":64,"column":20}},"64":{"start":{"line":65,"column":0},"end":{"line":65,"column":21}},"65":{"start":{"line":66,"column":0},"end":{"line":66,"column":41}},"66":{"start":{"line":67,"column":0},"end":{"line":67,"column":16}},"67":{"start":{"line":68,"column":0},"end":{"line":68,"column":43}},"68":{"start":{"line":69,"column":0},"end":{"line":69,"column":104}},"69":{"start":{"line":70,"column":0},"end":{"line":70,"column":8}},"70":{"start":{"line":71,"column":0},"end":{"line":71,"column":11}},"71":{"start":{"line":72,"column":0},"end":{"line":72,"column":6}},"73":{"start":{"line":74,"column":0},"end":{"line":74,"column":41}},"74":{"start":{"line":75,"column":0},"end":{"line":75,"column":25}},"75":{"start":{"line":76,"column":0},"end":{"line":76,"column":19}},"76":{"start":{"line":77,"column":0},"end":{"line":77,"column":56}},"77":{"start":{"line":78,"column":0},"end":{"line":78,"column":25}},"78":{"start":{"line":79,"column":0},"end":{"line":79,"column":84}},"79":{"start":{"line":80,"column":0},"end":{"line":80,"column":30}},"80":{"start":{"line":81,"column":0},"end":{"line":81,"column":8}},"81":{"start":{"line":82,"column":0},"end":{"line":82,"column":5}},"82":{"start":{"line":83,"column":0},"end":{"line":83,"column":56}},"83":{"start":{"line":84,"column":0},"end":{"line":84,"column":3}},"84":{"start":{"line":85,"column":0},"end":{"line":85,"column":2}},"87":{"start":{"line":88,"column":0},"end":{"line":88,"column":37}},"88":{"start":{"line":89,"column":0},"end":{"line":89,"column":20}},"89":{"start":{"line":90,"column":0},"end":{"line":90,"column":16}},"90":{"start":{"line":91,"column":0},"end":{"line":91,"column":29}},"91":{"start":{"line":92,"column":0},"end":{"line":92,"column":7}},"92":{"start":{"line":93,"column":0},"end":{"line":93,"column":20}},"93":{"start":{"line":94,"column":0},"end":{"line":94,"column":21}},"94":{"start":{"line":95,"column":0},"end":{"line":95,"column":42}},"95":{"start":{"line":96,"column":0},"end":{"line":96,"column":16}},"96":{"start":{"line":97,"column":0},"end":{"line":97,"column":44}},"97":{"start":{"line":98,"column":0},"end":{"line":98,"column":50}},"98":{"start":{"line":99,"column":0},"end":{"line":99,"column":43}},"99":{"start":{"line":100,"column":0},"end":{"line":100,"column":8}},"100":{"start":{"line":101,"column":0},"end":{"line":101,"column":33}},"101":{"start":{"line":102,"column":0},"end":{"line":102,"column":6}},"103":{"start":{"line":104,"column":0},"end":{"line":104,"column":41}},"104":{"start":{"line":105,"column":0},"end":{"line":105,"column":25}},"105":{"start":{"line":106,"column":0},"end":{"line":106,"column":19}},"106":{"start":{"line":107,"column":0},"end":{"line":107,"column":56}},"107":{"start":{"line":108,"column":0},"end":{"line":108,"column":25}},"108":{"start":{"line":109,"column":0},"end":{"line":109,"column":82}},"109":{"start":{"line":110,"column":0},"end":{"line":110,"column":30}},"110":{"start":{"line":111,"column":0},"end":{"line":111,"column":8}},"111":{"start":{"line":112,"column":0},"end":{"line":112,"column":5}},"112":{"start":{"line":113,"column":0},"end":{"line":113,"column":54}},"113":{"start":{"line":114,"column":0},"end":{"line":114,"column":3}},"114":{"start":{"line":115,"column":0},"end":{"line":115,"column":2}},"117":{"start":{"line":118,"column":0},"end":{"line":118,"column":33}},"118":{"start":{"line":119,"column":0},"end":{"line":119,"column":20}},"119":{"start":{"line":120,"column":0},"end":{"line":120,"column":19}},"120":{"start":{"line":121,"column":0},"end":{"line":121,"column":16}},"121":{"start":{"line":122,"column":0},"end":{"line":122,"column":29}},"122":{"start":{"line":123,"column":0},"end":{"line":123,"column":7}},"123":{"start":{"line":124,"column":0},"end":{"line":124,"column":33}},"124":{"start":{"line":125,"column":0},"end":{"line":125,"column":30}},"125":{"start":{"line":126,"column":0},"end":{"line":126,"column":15}},"126":{"start":{"line":127,"column":0},"end":{"line":127,"column":7}},"128":{"start":{"line":129,"column":0},"end":{"line":129,"column":20}},"129":{"start":{"line":130,"column":0},"end":{"line":130,"column":21}},"130":{"start":{"line":131,"column":0},"end":{"line":131,"column":43}},"131":{"start":{"line":132,"column":0},"end":{"line":132,"column":16}},"132":{"start":{"line":133,"column":0},"end":{"line":133,"column":44}},"133":{"start":{"line":134,"column":0},"end":{"line":134,"column":50}},"134":{"start":{"line":135,"column":0},"end":{"line":135,"column":43}},"135":{"start":{"line":136,"column":0},"end":{"line":136,"column":8}},"136":{"start":{"line":137,"column":0},"end":{"line":137,"column":11}},"137":{"start":{"line":138,"column":0},"end":{"line":138,"column":6}},"139":{"start":{"line":140,"column":0},"end":{"line":140,"column":41}},"140":{"start":{"line":141,"column":0},"end":{"line":141,"column":25}},"141":{"start":{"line":142,"column":0},"end":{"line":142,"column":19}},"142":{"start":{"line":143,"column":0},"end":{"line":143,"column":56}},"143":{"start":{"line":144,"column":0},"end":{"line":144,"column":25}},"144":{"start":{"line":145,"column":0},"end":{"line":145,"column":90}},"145":{"start":{"line":146,"column":0},"end":{"line":146,"column":30}},"146":{"start":{"line":147,"column":0},"end":{"line":147,"column":8}},"147":{"start":{"line":148,"column":0},"end":{"line":148,"column":5}},"148":{"start":{"line":149,"column":0},"end":{"line":149,"column":62}},"149":{"start":{"line":150,"column":0},"end":{"line":150,"column":3}},"150":{"start":{"line":151,"column":0},"end":{"line":151,"column":2}},"153":{"start":{"line":154,"column":0},"end":{"line":154,"column":46}},"154":{"start":{"line":155,"column":0},"end":{"line":155,"column":58}},"155":{"start":{"line":156,"column":0},"end":{"line":156,"column":16}},"156":{"start":{"line":157,"column":0},"end":{"line":157,"column":21}},"157":{"start":{"line":158,"column":0},"end":{"line":158,"column":33}},"158":{"start":{"line":159,"column":0},"end":{"line":159,"column":31}},"159":{"start":{"line":160,"column":0},"end":{"line":160,"column":52}},"161":{"start":{"line":162,"column":0},"end":{"line":162,"column":7}},"162":{"start":{"line":163,"column":0},"end":{"line":163,"column":48}},"163":{"start":{"line":164,"column":0},"end":{"line":164,"column":17}},"164":{"start":{"line":165,"column":0},"end":{"line":165,"column":64}},"165":{"start":{"line":166,"column":0},"end":{"line":166,"column":5}},"167":{"start":{"line":168,"column":0},"end":{"line":168,"column":43}},"168":{"start":{"line":169,"column":0},"end":{"line":169,"column":36}},"169":{"start":{"line":170,"column":0},"end":{"line":170,"column":25}},"170":{"start":{"line":171,"column":0},"end":{"line":171,"column":87}},"171":{"start":{"line":172,"column":0},"end":{"line":172,"column":12}},"172":{"start":{"line":173,"column":0},"end":{"line":173,"column":8}},"173":{"start":{"line":174,"column":0},"end":{"line":174,"column":5}},"176":{"start":{"line":177,"column":0},"end":{"line":177,"column":63}},"177":{"start":{"line":178,"column":0},"end":{"line":178,"column":55}},"178":{"start":{"line":179,"column":0},"end":{"line":179,"column":18}},"179":{"start":{"line":180,"column":0},"end":{"line":180,"column":31}},"180":{"start":{"line":181,"column":0},"end":{"line":181,"column":6}},"182":{"start":{"line":183,"column":0},"end":{"line":183,"column":8}},"183":{"start":{"line":184,"column":0},"end":{"line":184,"column":46}},"184":{"start":{"line":185,"column":0},"end":{"line":185,"column":37}},"185":{"start":{"line":186,"column":0},"end":{"line":186,"column":7}},"186":{"start":{"line":187,"column":0},"end":{"line":187,"column":49}},"187":{"start":{"line":188,"column":0},"end":{"line":188,"column":13}},"188":{"start":{"line":189,"column":0},"end":{"line":189,"column":5}},"190":{"start":{"line":191,"column":0},"end":{"line":191,"column":28}},"191":{"start":{"line":192,"column":0},"end":{"line":192,"column":11}},"192":{"start":{"line":193,"column":0},"end":{"line":193,"column":50}},"193":{"start":{"line":194,"column":0},"end":{"line":194,"column":53}},"194":{"start":{"line":195,"column":0},"end":{"line":195,"column":31}},"195":{"start":{"line":196,"column":0},"end":{"line":196,"column":45}},"196":{"start":{"line":197,"column":0},"end":{"line":197,"column":10}},"198":{"start":{"line":199,"column":0},"end":{"line":199,"column":44}},"199":{"start":{"line":200,"column":0},"end":{"line":200,"column":35}},"200":{"start":{"line":201,"column":0},"end":{"line":201,"column":13}},"201":{"start":{"line":202,"column":0},"end":{"line":202,"column":53}},"202":{"start":{"line":203,"column":0},"end":{"line":203,"column":34}},"203":{"start":{"line":204,"column":0},"end":{"line":204,"column":45}},"204":{"start":{"line":205,"column":0},"end":{"line":205,"column":45}},"205":{"start":{"line":206,"column":0},"end":{"line":206,"column":22}},"206":{"start":{"line":207,"column":0},"end":{"line":207,"column":40}},"207":{"start":{"line":208,"column":0},"end":{"line":208,"column":36}},"208":{"start":{"line":209,"column":0},"end":{"line":209,"column":14}},"209":{"start":{"line":210,"column":0},"end":{"line":210,"column":48}},"210":{"start":{"line":211,"column":0},"end":{"line":211,"column":12}},"212":{"start":{"line":213,"column":0},"end":{"line":213,"column":37}},"214":{"start":{"line":215,"column":0},"end":{"line":215,"column":32}},"215":{"start":{"line":216,"column":0},"end":{"line":216,"column":30}},"216":{"start":{"line":217,"column":0},"end":{"line":217,"column":36}},"217":{"start":{"line":218,"column":0},"end":{"line":218,"column":13}},"218":{"start":{"line":219,"column":0},"end":{"line":219,"column":16}},"219":{"start":{"line":220,"column":0},"end":{"line":220,"column":39}},"220":{"start":{"line":221,"column":0},"end":{"line":221,"column":32}},"221":{"start":{"line":222,"column":0},"end":{"line":222,"column":32}},"222":{"start":{"line":223,"column":0},"end":{"line":223,"column":20}},"223":{"start":{"line":224,"column":0},"end":{"line":224,"column":74}},"224":{"start":{"line":225,"column":0},"end":{"line":225,"column":13}},"225":{"start":{"line":226,"column":0},"end":{"line":226,"column":9}},"226":{"start":{"line":227,"column":0},"end":{"line":227,"column":23}},"227":{"start":{"line":228,"column":0},"end":{"line":228,"column":37}},"228":{"start":{"line":229,"column":0},"end":{"line":229,"column":40}},"229":{"start":{"line":230,"column":0},"end":{"line":230,"column":72}},"230":{"start":{"line":231,"column":0},"end":{"line":231,"column":16}},"231":{"start":{"line":232,"column":0},"end":{"line":232,"column":32}},"232":{"start":{"line":233,"column":0},"end":{"line":233,"column":20}},"233":{"start":{"line":234,"column":0},"end":{"line":234,"column":36}},"234":{"start":{"line":235,"column":0},"end":{"line":235,"column":31}},"235":{"start":{"line":236,"column":0},"end":{"line":236,"column":64}},"236":{"start":{"line":237,"column":0},"end":{"line":237,"column":13}},"237":{"start":{"line":238,"column":0},"end":{"line":238,"column":9}},"238":{"start":{"line":239,"column":0},"end":{"line":239,"column":7}},"239":{"start":{"line":240,"column":0},"end":{"line":240,"column":15}},"240":{"start":{"line":241,"column":0},"end":{"line":241,"column":19}},"241":{"start":{"line":242,"column":0},"end":{"line":242,"column":33}},"242":{"start":{"line":243,"column":0},"end":{"line":243,"column":36}},"243":{"start":{"line":244,"column":0},"end":{"line":244,"column":68}},"244":{"start":{"line":245,"column":0},"end":{"line":245,"column":12}},"245":{"start":{"line":246,"column":0},"end":{"line":246,"column":28}},"246":{"start":{"line":247,"column":0},"end":{"line":247,"column":16}},"247":{"start":{"line":248,"column":0},"end":{"line":248,"column":32}},"248":{"start":{"line":249,"column":0},"end":{"line":249,"column":27}},"249":{"start":{"line":250,"column":0},"end":{"line":250,"column":60}},"250":{"start":{"line":251,"column":0},"end":{"line":251,"column":9}},"251":{"start":{"line":252,"column":0},"end":{"line":252,"column":5}},"252":{"start":{"line":253,"column":0},"end":{"line":253,"column":3}},"253":{"start":{"line":254,"column":0},"end":{"line":254,"column":2}},"256":{"start":{"line":257,"column":0},"end":{"line":257,"column":45}},"257":{"start":{"line":258,"column":0},"end":{"line":258,"column":59}},"258":{"start":{"line":259,"column":0},"end":{"line":259,"column":16}},"259":{"start":{"line":260,"column":0},"end":{"line":260,"column":21}},"260":{"start":{"line":261,"column":0},"end":{"line":261,"column":77}},"261":{"start":{"line":262,"column":0},"end":{"line":262,"column":52}},"263":{"start":{"line":264,"column":0},"end":{"line":264,"column":7}},"264":{"start":{"line":265,"column":0},"end":{"line":265,"column":25}},"265":{"start":{"line":266,"column":0},"end":{"line":266,"column":59}},"266":{"start":{"line":267,"column":0},"end":{"line":267,"column":5}},"268":{"start":{"line":269,"column":0},"end":{"line":269,"column":43}},"269":{"start":{"line":270,"column":0},"end":{"line":270,"column":36}},"270":{"start":{"line":271,"column":0},"end":{"line":271,"column":68}},"271":{"start":{"line":272,"column":0},"end":{"line":272,"column":5}},"273":{"start":{"line":274,"column":0},"end":{"line":274,"column":50}},"274":{"start":{"line":275,"column":0},"end":{"line":275,"column":16}},"275":{"start":{"line":276,"column":0},"end":{"line":276,"column":20}},"276":{"start":{"line":277,"column":0},"end":{"line":277,"column":31}},"277":{"start":{"line":278,"column":0},"end":{"line":278,"column":6}},"279":{"start":{"line":280,"column":0},"end":{"line":280,"column":44}},"280":{"start":{"line":281,"column":0},"end":{"line":281,"column":59}},"281":{"start":{"line":282,"column":0},"end":{"line":282,"column":5}},"283":{"start":{"line":284,"column":0},"end":{"line":284,"column":52}},"284":{"start":{"line":285,"column":0},"end":{"line":285,"column":17}},"285":{"start":{"line":286,"column":0},"end":{"line":286,"column":49}},"286":{"start":{"line":287,"column":0},"end":{"line":287,"column":5}},"288":{"start":{"line":289,"column":0},"end":{"line":289,"column":55}},"289":{"start":{"line":290,"column":0},"end":{"line":290,"column":36}},"290":{"start":{"line":291,"column":0},"end":{"line":291,"column":35}},"291":{"start":{"line":292,"column":0},"end":{"line":292,"column":7}},"293":{"start":{"line":294,"column":0},"end":{"line":294,"column":23}},"294":{"start":{"line":295,"column":0},"end":{"line":295,"column":25}},"295":{"start":{"line":296,"column":0},"end":{"line":296,"column":9}},"296":{"start":{"line":297,"column":0},"end":{"line":297,"column":17}},"297":{"start":{"line":298,"column":0},"end":{"line":298,"column":30}},"298":{"start":{"line":299,"column":0},"end":{"line":299,"column":34}},"299":{"start":{"line":300,"column":0},"end":{"line":300,"column":40}},"300":{"start":{"line":301,"column":0},"end":{"line":301,"column":32}},"301":{"start":{"line":302,"column":0},"end":{"line":302,"column":10}},"302":{"start":{"line":303,"column":0},"end":{"line":303,"column":54}},"303":{"start":{"line":304,"column":0},"end":{"line":304,"column":8}},"304":{"start":{"line":305,"column":0},"end":{"line":305,"column":19}},"305":{"start":{"line":306,"column":0},"end":{"line":306,"column":9}},"306":{"start":{"line":307,"column":0},"end":{"line":307,"column":31}},"307":{"start":{"line":308,"column":0},"end":{"line":308,"column":32}},"308":{"start":{"line":309,"column":0},"end":{"line":309,"column":10}},"309":{"start":{"line":310,"column":0},"end":{"line":310,"column":49}},"310":{"start":{"line":311,"column":0},"end":{"line":311,"column":8}},"311":{"start":{"line":312,"column":0},"end":{"line":312,"column":7}},"314":{"start":{"line":315,"column":0},"end":{"line":315,"column":36}},"315":{"start":{"line":316,"column":0},"end":{"line":316,"column":56}},"316":{"start":{"line":317,"column":0},"end":{"line":317,"column":27}},"317":{"start":{"line":318,"column":0},"end":{"line":318,"column":67}},"318":{"start":{"line":319,"column":0},"end":{"line":319,"column":8}},"320":{"start":{"line":321,"column":0},"end":{"line":321,"column":52}},"321":{"start":{"line":322,"column":0},"end":{"line":322,"column":41}},"322":{"start":{"line":323,"column":0},"end":{"line":323,"column":42}},"323":{"start":{"line":324,"column":0},"end":{"line":324,"column":8}},"325":{"start":{"line":326,"column":0},"end":{"line":326,"column":54}},"326":{"start":{"line":327,"column":0},"end":{"line":327,"column":33}},"327":{"start":{"line":328,"column":0},"end":{"line":328,"column":20}},"328":{"start":{"line":329,"column":0},"end":{"line":329,"column":8}},"330":{"start":{"line":331,"column":0},"end":{"line":331,"column":41}},"331":{"start":{"line":332,"column":0},"end":{"line":332,"column":33}},"332":{"start":{"line":333,"column":0},"end":{"line":333,"column":33}},"333":{"start":{"line":334,"column":0},"end":{"line":334,"column":25}},"334":{"start":{"line":335,"column":0},"end":{"line":335,"column":30}},"335":{"start":{"line":336,"column":0},"end":{"line":336,"column":10}},"336":{"start":{"line":337,"column":0},"end":{"line":337,"column":7}},"337":{"start":{"line":338,"column":0},"end":{"line":338,"column":5}},"339":{"start":{"line":340,"column":0},"end":{"line":340,"column":35}},"340":{"start":{"line":341,"column":0},"end":{"line":341,"column":56}},"341":{"start":{"line":342,"column":0},"end":{"line":342,"column":30}},"342":{"start":{"line":343,"column":0},"end":{"line":343,"column":16}},"343":{"start":{"line":344,"column":0},"end":{"line":344,"column":79}},"344":{"start":{"line":345,"column":0},"end":{"line":345,"column":8}},"346":{"start":{"line":347,"column":0},"end":{"line":347,"column":52}},"347":{"start":{"line":348,"column":0},"end":{"line":348,"column":41}},"348":{"start":{"line":349,"column":0},"end":{"line":349,"column":42}},"349":{"start":{"line":350,"column":0},"end":{"line":350,"column":8}},"351":{"start":{"line":352,"column":0},"end":{"line":352,"column":54}},"352":{"start":{"line":353,"column":0},"end":{"line":353,"column":32}},"353":{"start":{"line":354,"column":0},"end":{"line":354,"column":20}},"354":{"start":{"line":355,"column":0},"end":{"line":355,"column":8}},"356":{"start":{"line":357,"column":0},"end":{"line":357,"column":41}},"357":{"start":{"line":358,"column":0},"end":{"line":358,"column":33}},"358":{"start":{"line":359,"column":0},"end":{"line":359,"column":33}},"359":{"start":{"line":360,"column":0},"end":{"line":360,"column":24}},"360":{"start":{"line":361,"column":0},"end":{"line":361,"column":30}},"361":{"start":{"line":362,"column":0},"end":{"line":362,"column":10}},"362":{"start":{"line":363,"column":0},"end":{"line":363,"column":7}},"363":{"start":{"line":364,"column":0},"end":{"line":364,"column":5}},"365":{"start":{"line":366,"column":0},"end":{"line":366,"column":31}},"366":{"start":{"line":367,"column":0},"end":{"line":367,"column":72}},"367":{"start":{"line":368,"column":0},"end":{"line":368,"column":19}},"368":{"start":{"line":369,"column":0},"end":{"line":369,"column":33}},"369":{"start":{"line":370,"column":0},"end":{"line":370,"column":36}},"370":{"start":{"line":371,"column":0},"end":{"line":371,"column":68}},"371":{"start":{"line":372,"column":0},"end":{"line":372,"column":12}},"372":{"start":{"line":373,"column":0},"end":{"line":373,"column":28}},"373":{"start":{"line":374,"column":0},"end":{"line":374,"column":16}},"374":{"start":{"line":375,"column":0},"end":{"line":375,"column":32}},"375":{"start":{"line":376,"column":0},"end":{"line":376,"column":27}},"376":{"start":{"line":377,"column":0},"end":{"line":377,"column":60}},"377":{"start":{"line":378,"column":0},"end":{"line":378,"column":9}},"378":{"start":{"line":379,"column":0},"end":{"line":379,"column":5}},"379":{"start":{"line":380,"column":0},"end":{"line":380,"column":3}},"380":{"start":{"line":381,"column":0},"end":{"line":381,"column":2}}},"s":{"0":0,"1":0,"2":0,"3":0,"5":0,"6":0,"7":0,"10":0,"47":0,"48":0,"49":0,"50":0,"52":0,"53":0,"54":0,"57":0,"58":0,"59":0,"60":0,"61":0,"63":0,"64":0,"65":0,"66":0,"67":0,"68":0,"69":0,"70":0,"71":0,"73":0,"74":0,"75":0,"76":0,"77":0,"78":0,"79":0,"80":0,"81":0,"82":0,"83":0,"84":0,"87":0,"88":0,"89":0,"90":0,"91":0,"92":0,"93":0,"94":0,"95":0,"96":0,"97":0,"98":0,"99":0,"100":0,"101":0,"103":0,"104":0,"105":0,"106":0,"107":0,"108":0,"109":0,"110":0,"111":0,"112":0,"113":0,"114":0,"117":0,"118":0,"119":0,"120":0,"121":0,"122":0,"123":0,"124":0,"125":0,"126":0,"128":0,"129":0,"130":0,"131":0,"132":0,"133":0,"134":0,"135":0,"136":0,"137":0,"139":0,"140":0,"141":0,"142":0,"143":0,"144":0,"145":0,"146":0,"147":0,"148":0,"149":0,"150":0,"153":0,"154":0,"155":0,"156":0,"157":0,"158":0,"159":0,"161":0,"162":0,"163":0,"164":0,"165":0,"167":0,"168":0,"169":0,"170":0,"171":0,"172":0,"173":0,"176":0,"177":0,"178":0,"179":0,"180":0,"182":0,"183":0,"184":0,"185":0,"186":0,"187":0,"188":0,"190":0,"191":0,"192":0,"193":0,"194":0,"195":0,"196":0,"198":0,"199":0,"200":0,"201":0,"202":0,"203":0,"204":0,"205":0,"206":0,"207":0,"208":0,"209":0,"210":0,"212":0,"214":0,"215":0,"216":0,"217":0,"218":0,"219":0,"220":0,"221":0,"222":0,"223":0,"224":0,"225":0,"226":0,"227":0,"228":0,"229":0,"230":0,"231":0,"232":0,"233":0,"234":0,"235":0,"236":0,"237":0,"238":0,"239":0,"240":0,"241":0,"242":0,"243":0,"244":0,"245":0,"246":0,"247":0,"248":0,"249":0,"250":0,"251":0,"252":0,"253":0,"256":0,"257":0,"258":0,"259":0,"260":0,"261":0,"263":0,"264":0,"265":0,"266":0,"268":0,"269":0,"270":0,"271":0,"273":0,"274":0,"275":0,"276":0,"277":0,"279":0,"280":0,"281":0,"283":0,"284":0,"285":0,"286":0,"288":0,"289":0,"290":0,"291":0,"293":0,"294":0,"295":0,"296":0,"297":0,"298":0,"299":0,"300":0,"301":0,"302":0,"303":0,"304":0,"305":0,"306":0,"307":0,"308":0,"309":0,"310":0,"311":0,"314":0,"315":0,"316":0,"317":0,"318":0,"320":0,"321":0,"322":0,"323":0,"325":0,"326":0,"327":0,"328":0,"330":0,"331":0,"332":0,"333":0,"334":0,"335":0,"336":0,"337":0,"339":0,"340":0,"341":0,"342":0,"343":0,"344":0,"346":0,"347":0,"348":0,"349":0,"351":0,"352":0,"353":0,"354":0,"356":0,"357":0,"358":0,"359":0,"360":0,"361":0,"362":0,"363":0,"365":0,"366":0,"367":0,"368":0,"369":0,"370":0,"371":0,"372":0,"373":0,"374":0,"375":0,"376":0,"377":0,"378":0,"379":0,"380":0},"branchMap":{"0":{"type":"branch","line":1,"loc":{"start":{"line":1,"column":10226},"end":{"line":381,"column":2}},"locations":[{"start":{"line":1,"column":10226},"end":{"line":381,"column":2}}]}},"b":{"0":[0]},"fnMap":{"0":{"name":"(empty-report)","decl":{"start":{"line":1,"column":10226},"end":{"line":381,"column":2}},"loc":{"start":{"line":1,"column":10226},"end":{"line":381,"column":2}},"line":1}},"f":{"0":0}} +,"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/src/controllers/product.controller.ts": {"path":"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/src/controllers/product.controller.ts","all":false,"statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":25}},"1":{"start":{"line":2,"column":0},"end":{"line":2,"column":34}},"2":{"start":{"line":3,"column":0},"end":{"line":3,"column":34}},"3":{"start":{"line":4,"column":0},"end":{"line":4,"column":48}},"4":{"start":{"line":5,"column":0},"end":{"line":5,"column":40}},"5":{"start":{"line":6,"column":0},"end":{"line":6,"column":46}},"8":{"start":{"line":9,"column":0},"end":{"line":9,"column":46}},"26":{"start":{"line":27,"column":0},"end":{"line":27,"column":19}},"27":{"start":{"line":28,"column":0},"end":{"line":28,"column":48}},"28":{"start":{"line":29,"column":0},"end":{"line":29,"column":42}},"29":{"start":{"line":30,"column":0},"end":{"line":30,"column":48}},"30":{"start":{"line":31,"column":0},"end":{"line":31,"column":3}},"33":{"start":{"line":34,"column":0},"end":{"line":34,"column":34}},"34":{"start":{"line":35,"column":0},"end":{"line":35,"column":15}},"35":{"start":{"line":36,"column":0},"end":{"line":36,"column":16}},"36":{"start":{"line":37,"column":0},"end":{"line":37,"column":21}},"37":{"start":{"line":38,"column":0},"end":{"line":38,"column":7}},"38":{"start":{"line":39,"column":0},"end":{"line":39,"column":52}},"39":{"start":{"line":40,"column":0},"end":{"line":40,"column":16}},"40":{"start":{"line":41,"column":0},"end":{"line":41,"column":9}},"41":{"start":{"line":42,"column":0},"end":{"line":42,"column":22}},"42":{"start":{"line":43,"column":0},"end":{"line":43,"column":23}},"43":{"start":{"line":44,"column":0},"end":{"line":44,"column":17}},"44":{"start":{"line":45,"column":0},"end":{"line":45,"column":24}},"45":{"start":{"line":46,"column":0},"end":{"line":46,"column":23}},"46":{"start":{"line":47,"column":0},"end":{"line":47,"column":22}},"47":{"start":{"line":48,"column":0},"end":{"line":48,"column":27}},"48":{"start":{"line":49,"column":0},"end":{"line":49,"column":12}},"49":{"start":{"line":50,"column":0},"end":{"line":50,"column":37}},"50":{"start":{"line":51,"column":0},"end":{"line":51,"column":10}},"51":{"start":{"line":52,"column":0},"end":{"line":52,"column":8}},"52":{"start":{"line":53,"column":0},"end":{"line":53,"column":7}},"54":{"start":{"line":55,"column":0},"end":{"line":55,"column":20}},"55":{"start":{"line":56,"column":0},"end":{"line":56,"column":51}},"56":{"start":{"line":57,"column":0},"end":{"line":57,"column":5}},"58":{"start":{"line":59,"column":0},"end":{"line":59,"column":39}},"59":{"start":{"line":60,"column":0},"end":{"line":60,"column":19}},"60":{"start":{"line":61,"column":0},"end":{"line":61,"column":36}},"61":{"start":{"line":62,"column":0},"end":{"line":62,"column":68}},"62":{"start":{"line":63,"column":0},"end":{"line":63,"column":13}},"63":{"start":{"line":64,"column":0},"end":{"line":64,"column":5}},"64":{"start":{"line":65,"column":0},"end":{"line":65,"column":26}},"65":{"start":{"line":66,"column":0},"end":{"line":66,"column":14}},"66":{"start":{"line":67,"column":0},"end":{"line":67,"column":74}},"67":{"start":{"line":68,"column":0},"end":{"line":68,"column":7}},"68":{"start":{"line":69,"column":0},"end":{"line":69,"column":3}},"69":{"start":{"line":70,"column":0},"end":{"line":70,"column":2}},"72":{"start":{"line":73,"column":0},"end":{"line":73,"column":33}},"73":{"start":{"line":74,"column":0},"end":{"line":74,"column":38}},"74":{"start":{"line":75,"column":0},"end":{"line":75,"column":16}},"75":{"start":{"line":76,"column":0},"end":{"line":76,"column":21}},"76":{"start":{"line":77,"column":0},"end":{"line":77,"column":7}},"77":{"start":{"line":78,"column":0},"end":{"line":78,"column":48}},"78":{"start":{"line":79,"column":0},"end":{"line":79,"column":42}},"79":{"start":{"line":80,"column":0},"end":{"line":80,"column":16}},"80":{"start":{"line":81,"column":0},"end":{"line":81,"column":9}},"81":{"start":{"line":82,"column":0},"end":{"line":82,"column":22}},"82":{"start":{"line":83,"column":0},"end":{"line":83,"column":23}},"83":{"start":{"line":84,"column":0},"end":{"line":84,"column":17}},"84":{"start":{"line":85,"column":0},"end":{"line":85,"column":24}},"85":{"start":{"line":86,"column":0},"end":{"line":86,"column":23}},"86":{"start":{"line":87,"column":0},"end":{"line":87,"column":22}},"87":{"start":{"line":88,"column":0},"end":{"line":88,"column":23}},"88":{"start":{"line":89,"column":0},"end":{"line":89,"column":27}},"89":{"start":{"line":90,"column":0},"end":{"line":90,"column":12}},"90":{"start":{"line":91,"column":0},"end":{"line":91,"column":10}},"91":{"start":{"line":92,"column":0},"end":{"line":92,"column":9}},"92":{"start":{"line":93,"column":0},"end":{"line":93,"column":29}},"93":{"start":{"line":94,"column":0},"end":{"line":94,"column":63}},"94":{"start":{"line":95,"column":0},"end":{"line":95,"column":26}},"95":{"start":{"line":96,"column":0},"end":{"line":96,"column":20}},"96":{"start":{"line":97,"column":0},"end":{"line":97,"column":13}},"97":{"start":{"line":98,"column":0},"end":{"line":98,"column":26}},"98":{"start":{"line":99,"column":0},"end":{"line":99,"column":27}},"99":{"start":{"line":100,"column":0},"end":{"line":100,"column":21}},"100":{"start":{"line":101,"column":0},"end":{"line":101,"column":28}},"101":{"start":{"line":102,"column":0},"end":{"line":102,"column":27}},"102":{"start":{"line":103,"column":0},"end":{"line":103,"column":26}},"103":{"start":{"line":104,"column":0},"end":{"line":104,"column":27}},"104":{"start":{"line":105,"column":0},"end":{"line":105,"column":31}},"105":{"start":{"line":106,"column":0},"end":{"line":106,"column":16}},"106":{"start":{"line":107,"column":0},"end":{"line":107,"column":14}},"107":{"start":{"line":108,"column":0},"end":{"line":108,"column":12}},"108":{"start":{"line":109,"column":0},"end":{"line":109,"column":10}},"109":{"start":{"line":110,"column":0},"end":{"line":110,"column":8}},"110":{"start":{"line":111,"column":0},"end":{"line":111,"column":7}},"112":{"start":{"line":113,"column":0},"end":{"line":113,"column":24}},"113":{"start":{"line":114,"column":0},"end":{"line":114,"column":51}},"114":{"start":{"line":115,"column":0},"end":{"line":115,"column":5}},"116":{"start":{"line":117,"column":0},"end":{"line":117,"column":52}},"117":{"start":{"line":118,"column":0},"end":{"line":118,"column":19}},"118":{"start":{"line":119,"column":0},"end":{"line":119,"column":36}},"119":{"start":{"line":120,"column":0},"end":{"line":120,"column":68}},"120":{"start":{"line":121,"column":0},"end":{"line":121,"column":13}},"121":{"start":{"line":122,"column":0},"end":{"line":122,"column":5}},"122":{"start":{"line":123,"column":0},"end":{"line":123,"column":26}},"123":{"start":{"line":124,"column":0},"end":{"line":124,"column":80}},"124":{"start":{"line":125,"column":0},"end":{"line":125,"column":7}},"125":{"start":{"line":126,"column":0},"end":{"line":126,"column":3}},"126":{"start":{"line":127,"column":0},"end":{"line":127,"column":2}},"129":{"start":{"line":130,"column":0},"end":{"line":130,"column":35}},"130":{"start":{"line":131,"column":0},"end":{"line":131,"column":35}},"131":{"start":{"line":132,"column":0},"end":{"line":132,"column":16}},"132":{"start":{"line":133,"column":0},"end":{"line":133,"column":21}},"133":{"start":{"line":134,"column":0},"end":{"line":134,"column":7}},"134":{"start":{"line":135,"column":0},"end":{"line":135,"column":56}},"135":{"start":{"line":136,"column":0},"end":{"line":136,"column":43}},"136":{"start":{"line":137,"column":0},"end":{"line":137,"column":7}},"138":{"start":{"line":139,"column":0},"end":{"line":139,"column":24}},"139":{"start":{"line":140,"column":0},"end":{"line":140,"column":65}},"140":{"start":{"line":141,"column":0},"end":{"line":141,"column":5}},"142":{"start":{"line":143,"column":0},"end":{"line":143,"column":53}},"143":{"start":{"line":144,"column":0},"end":{"line":144,"column":19}},"144":{"start":{"line":145,"column":0},"end":{"line":145,"column":36}},"145":{"start":{"line":146,"column":0},"end":{"line":146,"column":68}},"146":{"start":{"line":147,"column":0},"end":{"line":147,"column":13}},"147":{"start":{"line":148,"column":0},"end":{"line":148,"column":5}},"148":{"start":{"line":149,"column":0},"end":{"line":149,"column":26}},"149":{"start":{"line":150,"column":0},"end":{"line":150,"column":14}},"150":{"start":{"line":151,"column":0},"end":{"line":151,"column":79}},"151":{"start":{"line":152,"column":0},"end":{"line":152,"column":7}},"152":{"start":{"line":153,"column":0},"end":{"line":153,"column":3}},"153":{"start":{"line":154,"column":0},"end":{"line":154,"column":2}},"156":{"start":{"line":157,"column":0},"end":{"line":157,"column":36}},"157":{"start":{"line":158,"column":0},"end":{"line":158,"column":61}},"158":{"start":{"line":159,"column":0},"end":{"line":159,"column":16}},"159":{"start":{"line":160,"column":0},"end":{"line":160,"column":21}},"160":{"start":{"line":161,"column":0},"end":{"line":161,"column":21}},"161":{"start":{"line":162,"column":0},"end":{"line":162,"column":7}},"162":{"start":{"line":163,"column":0},"end":{"line":163,"column":60}},"163":{"start":{"line":164,"column":0},"end":{"line":164,"column":62}},"164":{"start":{"line":165,"column":0},"end":{"line":165,"column":5}},"166":{"start":{"line":167,"column":0},"end":{"line":167,"column":77}},"169":{"start":{"line":170,"column":0},"end":{"line":170,"column":75}},"170":{"start":{"line":171,"column":0},"end":{"line":171,"column":57}},"171":{"start":{"line":172,"column":0},"end":{"line":172,"column":5}},"173":{"start":{"line":174,"column":0},"end":{"line":174,"column":25}},"174":{"start":{"line":175,"column":0},"end":{"line":175,"column":18}},"175":{"start":{"line":176,"column":0},"end":{"line":176,"column":34}},"176":{"start":{"line":177,"column":0},"end":{"line":177,"column":28}},"177":{"start":{"line":178,"column":0},"end":{"line":178,"column":28}},"178":{"start":{"line":179,"column":0},"end":{"line":179,"column":6}},"180":{"start":{"line":181,"column":0},"end":{"line":181,"column":19}},"181":{"start":{"line":182,"column":0},"end":{"line":182,"column":67}},"182":{"start":{"line":183,"column":0},"end":{"line":183,"column":22}},"183":{"start":{"line":184,"column":0},"end":{"line":184,"column":35}},"184":{"start":{"line":185,"column":0},"end":{"line":185,"column":31}},"186":{"start":{"line":187,"column":0},"end":{"line":187,"column":59}},"189":{"start":{"line":190,"column":0},"end":{"line":190,"column":35}},"190":{"start":{"line":191,"column":0},"end":{"line":191,"column":5}},"192":{"start":{"line":193,"column":0},"end":{"line":193,"column":53}},"194":{"start":{"line":195,"column":0},"end":{"line":195,"column":26}},"195":{"start":{"line":196,"column":0},"end":{"line":196,"column":46}},"196":{"start":{"line":197,"column":0},"end":{"line":197,"column":22}},"197":{"start":{"line":198,"column":0},"end":{"line":198,"column":7}},"198":{"start":{"line":199,"column":0},"end":{"line":199,"column":19}},"199":{"start":{"line":200,"column":0},"end":{"line":200,"column":36}},"200":{"start":{"line":201,"column":0},"end":{"line":201,"column":68}},"201":{"start":{"line":202,"column":0},"end":{"line":202,"column":13}},"202":{"start":{"line":203,"column":0},"end":{"line":203,"column":5}},"203":{"start":{"line":204,"column":0},"end":{"line":204,"column":16}},"204":{"start":{"line":205,"column":0},"end":{"line":205,"column":3}},"205":{"start":{"line":206,"column":0},"end":{"line":206,"column":2}},"208":{"start":{"line":209,"column":0},"end":{"line":209,"column":36}},"209":{"start":{"line":210,"column":0},"end":{"line":210,"column":69}},"210":{"start":{"line":211,"column":0},"end":{"line":211,"column":16}},"211":{"start":{"line":212,"column":0},"end":{"line":212,"column":21}},"212":{"start":{"line":213,"column":0},"end":{"line":213,"column":7}},"213":{"start":{"line":214,"column":0},"end":{"line":214,"column":65}},"214":{"start":{"line":215,"column":0},"end":{"line":215,"column":19}},"215":{"start":{"line":216,"column":0},"end":{"line":216,"column":51}},"216":{"start":{"line":217,"column":0},"end":{"line":217,"column":5}},"218":{"start":{"line":219,"column":0},"end":{"line":219,"column":19}},"219":{"start":{"line":220,"column":0},"end":{"line":220,"column":67}},"220":{"start":{"line":221,"column":0},"end":{"line":221,"column":22}},"221":{"start":{"line":222,"column":0},"end":{"line":222,"column":31}},"223":{"start":{"line":224,"column":0},"end":{"line":224,"column":56}},"226":{"start":{"line":227,"column":0},"end":{"line":227,"column":35}},"227":{"start":{"line":228,"column":0},"end":{"line":228,"column":5}},"229":{"start":{"line":230,"column":0},"end":{"line":230,"column":25}},"230":{"start":{"line":231,"column":0},"end":{"line":231,"column":18}},"231":{"start":{"line":232,"column":0},"end":{"line":232,"column":28}},"232":{"start":{"line":233,"column":0},"end":{"line":233,"column":6}},"234":{"start":{"line":235,"column":0},"end":{"line":235,"column":62}},"235":{"start":{"line":236,"column":0},"end":{"line":236,"column":42}},"236":{"start":{"line":237,"column":0},"end":{"line":237,"column":7}},"238":{"start":{"line":239,"column":0},"end":{"line":239,"column":29}},"239":{"start":{"line":240,"column":0},"end":{"line":240,"column":58}},"240":{"start":{"line":241,"column":0},"end":{"line":241,"column":5}},"242":{"start":{"line":243,"column":0},"end":{"line":243,"column":70}},"243":{"start":{"line":244,"column":0},"end":{"line":244,"column":19}},"244":{"start":{"line":245,"column":0},"end":{"line":245,"column":36}},"245":{"start":{"line":246,"column":0},"end":{"line":246,"column":68}},"246":{"start":{"line":247,"column":0},"end":{"line":247,"column":13}},"247":{"start":{"line":248,"column":0},"end":{"line":248,"column":5}},"248":{"start":{"line":249,"column":0},"end":{"line":249,"column":26}},"249":{"start":{"line":250,"column":0},"end":{"line":250,"column":14}},"250":{"start":{"line":251,"column":0},"end":{"line":251,"column":74}},"251":{"start":{"line":252,"column":0},"end":{"line":252,"column":7}},"252":{"start":{"line":253,"column":0},"end":{"line":253,"column":3}},"253":{"start":{"line":254,"column":0},"end":{"line":254,"column":2}},"256":{"start":{"line":257,"column":0},"end":{"line":257,"column":36}},"257":{"start":{"line":258,"column":0},"end":{"line":258,"column":38}},"258":{"start":{"line":259,"column":0},"end":{"line":259,"column":16}},"259":{"start":{"line":260,"column":0},"end":{"line":260,"column":21}},"260":{"start":{"line":261,"column":0},"end":{"line":261,"column":7}},"261":{"start":{"line":262,"column":0},"end":{"line":262,"column":65}},"262":{"start":{"line":263,"column":0},"end":{"line":263,"column":19}},"263":{"start":{"line":264,"column":0},"end":{"line":264,"column":51}},"264":{"start":{"line":265,"column":0},"end":{"line":265,"column":5}},"266":{"start":{"line":267,"column":0},"end":{"line":267,"column":48}},"267":{"start":{"line":268,"column":0},"end":{"line":268,"column":42}},"268":{"start":{"line":269,"column":0},"end":{"line":269,"column":7}},"270":{"start":{"line":271,"column":0},"end":{"line":271,"column":29}},"271":{"start":{"line":272,"column":0},"end":{"line":272,"column":58}},"272":{"start":{"line":273,"column":0},"end":{"line":273,"column":5}},"274":{"start":{"line":275,"column":0},"end":{"line":275,"column":79}},"275":{"start":{"line":276,"column":0},"end":{"line":276,"column":19}},"276":{"start":{"line":277,"column":0},"end":{"line":277,"column":36}},"277":{"start":{"line":278,"column":0},"end":{"line":278,"column":68}},"278":{"start":{"line":279,"column":0},"end":{"line":279,"column":13}},"279":{"start":{"line":280,"column":0},"end":{"line":280,"column":5}},"280":{"start":{"line":281,"column":0},"end":{"line":281,"column":26}},"281":{"start":{"line":282,"column":0},"end":{"line":282,"column":14}},"282":{"start":{"line":283,"column":0},"end":{"line":283,"column":74}},"283":{"start":{"line":284,"column":0},"end":{"line":284,"column":7}},"284":{"start":{"line":285,"column":0},"end":{"line":285,"column":3}},"285":{"start":{"line":286,"column":0},"end":{"line":286,"column":2}}},"s":{"0":1,"1":1,"2":1,"3":1,"4":1,"5":1,"8":1,"26":1,"27":1,"28":1,"29":1,"30":1,"33":1,"34":0,"35":0,"36":0,"37":0,"38":0,"39":0,"40":0,"41":0,"42":0,"43":0,"44":0,"45":0,"46":0,"47":0,"48":0,"49":0,"50":0,"51":0,"52":0,"54":0,"55":0,"56":0,"58":0,"59":0,"60":0,"61":0,"62":0,"63":0,"64":0,"65":0,"66":0,"67":0,"68":0,"69":0,"72":1,"73":0,"74":0,"75":0,"76":0,"77":0,"78":0,"79":0,"80":0,"81":0,"82":0,"83":0,"84":0,"85":0,"86":0,"87":0,"88":0,"89":0,"90":0,"91":0,"92":0,"93":0,"94":0,"95":0,"96":0,"97":0,"98":0,"99":0,"100":0,"101":0,"102":0,"103":0,"104":0,"105":0,"106":0,"107":0,"108":0,"109":0,"110":0,"112":0,"113":0,"114":0,"116":0,"117":0,"118":0,"119":0,"120":0,"121":0,"122":0,"123":0,"124":0,"125":0,"126":0,"129":1,"130":0,"131":0,"132":0,"133":0,"134":0,"135":0,"136":0,"138":0,"139":0,"140":0,"142":0,"143":0,"144":0,"145":0,"146":0,"147":0,"148":0,"149":0,"150":0,"151":0,"152":0,"153":0,"156":1,"157":0,"158":0,"159":0,"160":0,"161":0,"162":0,"163":0,"164":0,"166":0,"169":0,"170":0,"171":0,"173":0,"174":0,"175":0,"176":0,"177":0,"178":0,"180":0,"181":0,"182":0,"183":0,"184":0,"186":0,"189":0,"190":0,"192":0,"194":0,"195":0,"196":0,"197":0,"198":0,"199":0,"200":0,"201":0,"202":0,"203":0,"204":0,"205":0,"208":1,"209":0,"210":0,"211":0,"212":0,"213":0,"214":0,"215":0,"216":0,"218":0,"219":0,"220":0,"221":0,"223":0,"226":0,"227":0,"229":0,"230":0,"231":0,"232":0,"234":0,"235":0,"236":0,"238":0,"239":0,"240":0,"242":0,"243":0,"244":0,"245":0,"246":0,"247":0,"248":0,"249":0,"250":0,"251":0,"252":0,"253":0,"256":1,"257":0,"258":0,"259":0,"260":0,"261":0,"262":0,"263":0,"264":0,"266":0,"267":0,"268":0,"270":0,"271":0,"272":0,"274":0,"275":0,"276":0,"277":0,"278":0,"279":0,"280":0,"281":0,"282":0,"283":0,"284":0,"285":0},"branchMap":{},"b":{},"fnMap":{"0":{"name":"allProducts","decl":{"start":{"line":34,"column":27},"end":{"line":70,"column":2}},"loc":{"start":{"line":34,"column":27},"end":{"line":70,"column":2}},"line":34},"1":{"name":"getProduct","decl":{"start":{"line":73,"column":26},"end":{"line":127,"column":2}},"loc":{"start":{"line":73,"column":26},"end":{"line":127,"column":2}},"line":73},"2":{"name":"userProducts","decl":{"start":{"line":130,"column":28},"end":{"line":154,"column":2}},"loc":{"start":{"line":130,"column":28},"end":{"line":154,"column":2}},"line":130},"3":{"name":"createProduct","decl":{"start":{"line":157,"column":29},"end":{"line":206,"column":2}},"loc":{"start":{"line":157,"column":29},"end":{"line":206,"column":2}},"line":157},"4":{"name":"updateProduct","decl":{"start":{"line":209,"column":29},"end":{"line":254,"column":2}},"loc":{"start":{"line":209,"column":29},"end":{"line":254,"column":2}},"line":209},"5":{"name":"removeProduct","decl":{"start":{"line":257,"column":29},"end":{"line":286,"column":2}},"loc":{"start":{"line":257,"column":29},"end":{"line":286,"column":2}},"line":257}},"f":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0}} +,"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/src/controllers/product.search.controller.ts": {"path":"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/src/controllers/product.search.controller.ts","all":false,"statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":34}},"1":{"start":{"line":2,"column":0},"end":{"line":2,"column":48}},"2":{"start":{"line":3,"column":0},"end":{"line":3,"column":40}},"3":{"start":{"line":4,"column":0},"end":{"line":4,"column":45}},"5":{"start":{"line":6,"column":0},"end":{"line":6,"column":46}},"26":{"start":{"line":27,"column":0},"end":{"line":27,"column":42}},"27":{"start":{"line":28,"column":0},"end":{"line":28,"column":62}},"28":{"start":{"line":29,"column":0},"end":{"line":29,"column":16}},"29":{"start":{"line":30,"column":0},"end":{"line":30,"column":21}},"30":{"start":{"line":31,"column":0},"end":{"line":31,"column":7}},"31":{"start":{"line":32,"column":0},"end":{"line":32,"column":11}},"32":{"start":{"line":33,"column":0},"end":{"line":33,"column":18}},"33":{"start":{"line":34,"column":0},"end":{"line":34,"column":25}},"34":{"start":{"line":35,"column":0},"end":{"line":35,"column":15}},"35":{"start":{"line":36,"column":0},"end":{"line":36,"column":15}},"36":{"start":{"line":37,"column":0},"end":{"line":37,"column":20}},"37":{"start":{"line":38,"column":0},"end":{"line":38,"column":16}},"38":{"start":{"line":39,"column":0},"end":{"line":39,"column":17}},"39":{"start":{"line":40,"column":0},"end":{"line":40,"column":19}},"40":{"start":{"line":41,"column":0},"end":{"line":41,"column":18}},"43":{"start":{"line":44,"column":0},"end":{"line":44,"column":35}},"44":{"start":{"line":45,"column":0},"end":{"line":45,"column":37}},"45":{"start":{"line":46,"column":0},"end":{"line":46,"column":40}},"46":{"start":{"line":47,"column":0},"end":{"line":47,"column":53}},"47":{"start":{"line":48,"column":0},"end":{"line":48,"column":5}},"48":{"start":{"line":49,"column":0},"end":{"line":49,"column":60}},"49":{"start":{"line":50,"column":0},"end":{"line":50,"column":80}},"50":{"start":{"line":51,"column":0},"end":{"line":51,"column":5}},"52":{"start":{"line":53,"column":0},"end":{"line":53,"column":52}},"53":{"start":{"line":54,"column":0},"end":{"line":54,"column":53}},"56":{"start":{"line":57,"column":0},"end":{"line":57,"column":30}},"57":{"start":{"line":58,"column":0},"end":{"line":58,"column":33}},"58":{"start":{"line":59,"column":0},"end":{"line":59,"column":38}},"59":{"start":{"line":60,"column":0},"end":{"line":60,"column":8}},"60":{"start":{"line":61,"column":0},"end":{"line":61,"column":5}},"62":{"start":{"line":63,"column":0},"end":{"line":63,"column":45}},"63":{"start":{"line":64,"column":0},"end":{"line":64,"column":32}},"64":{"start":{"line":65,"column":0},"end":{"line":65,"column":37}},"65":{"start":{"line":66,"column":0},"end":{"line":66,"column":8}},"66":{"start":{"line":67,"column":0},"end":{"line":67,"column":5}},"69":{"start":{"line":70,"column":0},"end":{"line":70,"column":63}},"70":{"start":{"line":71,"column":0},"end":{"line":71,"column":63}},"71":{"start":{"line":72,"column":0},"end":{"line":72,"column":59}},"72":{"start":{"line":73,"column":0},"end":{"line":73,"column":47}},"73":{"start":{"line":74,"column":0},"end":{"line":74,"column":62}},"74":{"start":{"line":75,"column":0},"end":{"line":75,"column":7}},"75":{"start":{"line":76,"column":0},"end":{"line":76,"column":32}},"76":{"start":{"line":77,"column":0},"end":{"line":77,"column":27}},"77":{"start":{"line":78,"column":0},"end":{"line":78,"column":63}},"78":{"start":{"line":79,"column":0},"end":{"line":79,"column":14}},"79":{"start":{"line":80,"column":0},"end":{"line":80,"column":10}},"80":{"start":{"line":81,"column":0},"end":{"line":81,"column":7}},"81":{"start":{"line":82,"column":0},"end":{"line":82,"column":27}},"82":{"start":{"line":83,"column":0},"end":{"line":83,"column":43}},"83":{"start":{"line":84,"column":0},"end":{"line":84,"column":8}},"84":{"start":{"line":85,"column":0},"end":{"line":85,"column":5}},"86":{"start":{"line":87,"column":0},"end":{"line":87,"column":31}},"87":{"start":{"line":88,"column":0},"end":{"line":88,"column":35}},"88":{"start":{"line":89,"column":0},"end":{"line":89,"column":5}},"91":{"start":{"line":92,"column":0},"end":{"line":92,"column":63}},"92":{"start":{"line":93,"column":0},"end":{"line":93,"column":52}},"93":{"start":{"line":94,"column":0},"end":{"line":94,"column":79}},"94":{"start":{"line":95,"column":0},"end":{"line":95,"column":5}},"96":{"start":{"line":97,"column":0},"end":{"line":97,"column":50}},"97":{"start":{"line":98,"column":0},"end":{"line":98,"column":41}},"98":{"start":{"line":99,"column":0},"end":{"line":99,"column":22}},"99":{"start":{"line":100,"column":0},"end":{"line":100,"column":13}},"100":{"start":{"line":101,"column":0},"end":{"line":101,"column":16}},"101":{"start":{"line":102,"column":0},"end":{"line":102,"column":9}},"102":{"start":{"line":103,"column":0},"end":{"line":103,"column":22}},"103":{"start":{"line":104,"column":0},"end":{"line":104,"column":23}},"104":{"start":{"line":105,"column":0},"end":{"line":105,"column":17}},"105":{"start":{"line":106,"column":0},"end":{"line":106,"column":24}},"106":{"start":{"line":107,"column":0},"end":{"line":107,"column":23}},"107":{"start":{"line":108,"column":0},"end":{"line":108,"column":22}},"108":{"start":{"line":109,"column":0},"end":{"line":109,"column":27}},"109":{"start":{"line":110,"column":0},"end":{"line":110,"column":12}},"110":{"start":{"line":111,"column":0},"end":{"line":111,"column":10}},"111":{"start":{"line":112,"column":0},"end":{"line":112,"column":9}},"112":{"start":{"line":113,"column":0},"end":{"line":113,"column":29}},"113":{"start":{"line":114,"column":0},"end":{"line":114,"column":50}},"114":{"start":{"line":115,"column":0},"end":{"line":115,"column":18}},"115":{"start":{"line":116,"column":0},"end":{"line":116,"column":19}},"116":{"start":{"line":117,"column":0},"end":{"line":117,"column":12}},"117":{"start":{"line":118,"column":0},"end":{"line":118,"column":20}},"118":{"start":{"line":119,"column":0},"end":{"line":119,"column":13}},"119":{"start":{"line":120,"column":0},"end":{"line":120,"column":26}},"120":{"start":{"line":121,"column":0},"end":{"line":121,"column":27}},"121":{"start":{"line":122,"column":0},"end":{"line":122,"column":21}},"122":{"start":{"line":123,"column":0},"end":{"line":123,"column":28}},"123":{"start":{"line":124,"column":0},"end":{"line":124,"column":27}},"124":{"start":{"line":125,"column":0},"end":{"line":125,"column":26}},"125":{"start":{"line":126,"column":0},"end":{"line":126,"column":31}},"126":{"start":{"line":127,"column":0},"end":{"line":127,"column":16}},"127":{"start":{"line":128,"column":0},"end":{"line":128,"column":14}},"128":{"start":{"line":129,"column":0},"end":{"line":129,"column":12}},"129":{"start":{"line":130,"column":0},"end":{"line":130,"column":26}},"130":{"start":{"line":131,"column":0},"end":{"line":131,"column":10}},"131":{"start":{"line":132,"column":0},"end":{"line":132,"column":8}},"132":{"start":{"line":133,"column":0},"end":{"line":133,"column":7}},"134":{"start":{"line":135,"column":0},"end":{"line":135,"column":24}},"135":{"start":{"line":136,"column":0},"end":{"line":136,"column":28}},"136":{"start":{"line":137,"column":0},"end":{"line":137,"column":66}},"137":{"start":{"line":138,"column":0},"end":{"line":138,"column":28}},"138":{"start":{"line":139,"column":0},"end":{"line":139,"column":9}},"139":{"start":{"line":140,"column":0},"end":{"line":140,"column":13}},"140":{"start":{"line":141,"column":0},"end":{"line":141,"column":5}},"142":{"start":{"line":143,"column":0},"end":{"line":143,"column":26}},"143":{"start":{"line":144,"column":0},"end":{"line":144,"column":26}},"144":{"start":{"line":145,"column":0},"end":{"line":145,"column":7}},"145":{"start":{"line":146,"column":0},"end":{"line":146,"column":19}},"146":{"start":{"line":147,"column":0},"end":{"line":147,"column":36}},"147":{"start":{"line":148,"column":0},"end":{"line":148,"column":68}},"148":{"start":{"line":149,"column":0},"end":{"line":149,"column":13}},"149":{"start":{"line":150,"column":0},"end":{"line":150,"column":5}},"150":{"start":{"line":151,"column":0},"end":{"line":151,"column":26}},"151":{"start":{"line":152,"column":0},"end":{"line":152,"column":14}},"152":{"start":{"line":153,"column":0},"end":{"line":153,"column":76}},"153":{"start":{"line":154,"column":0},"end":{"line":154,"column":7}},"154":{"start":{"line":155,"column":0},"end":{"line":155,"column":3}},"155":{"start":{"line":156,"column":0},"end":{"line":156,"column":2}}},"s":{"0":1,"1":1,"2":1,"3":1,"5":1,"26":1,"27":0,"28":0,"29":0,"30":0,"31":0,"32":0,"33":0,"34":0,"35":0,"36":0,"37":0,"38":0,"39":0,"40":0,"43":0,"44":0,"45":0,"46":0,"47":0,"48":0,"49":0,"50":0,"52":0,"53":0,"56":0,"57":0,"58":0,"59":0,"60":0,"62":0,"63":0,"64":0,"65":0,"66":0,"69":0,"70":0,"71":0,"72":0,"73":0,"74":0,"75":0,"76":0,"77":0,"78":0,"79":0,"80":0,"81":0,"82":0,"83":0,"84":0,"86":0,"87":0,"88":0,"91":0,"92":0,"93":0,"94":0,"96":0,"97":0,"98":0,"99":0,"100":0,"101":0,"102":0,"103":0,"104":0,"105":0,"106":0,"107":0,"108":0,"109":0,"110":0,"111":0,"112":0,"113":0,"114":0,"115":0,"116":0,"117":0,"118":0,"119":0,"120":0,"121":0,"122":0,"123":0,"124":0,"125":0,"126":0,"127":0,"128":0,"129":0,"130":0,"131":0,"132":0,"134":0,"135":0,"136":0,"137":0,"138":0,"139":0,"140":0,"142":0,"143":0,"144":0,"145":0,"146":0,"147":0,"148":0,"149":0,"150":0,"151":0,"152":0,"153":0,"154":0,"155":0},"branchMap":{},"b":{},"fnMap":{"0":{"name":"getAllProductSearch","decl":{"start":{"line":27,"column":35},"end":{"line":156,"column":2}},"loc":{"start":{"line":27,"column":35},"end":{"line":156,"column":2}},"line":27}},"f":{"0":0}} +,"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/src/controllers/review.controller.ts": {"path":"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/src/controllers/review.controller.ts","all":false,"statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":34}},"1":{"start":{"line":2,"column":0},"end":{"line":2,"column":48}},"2":{"start":{"line":3,"column":0},"end":{"line":3,"column":40}},"3":{"start":{"line":4,"column":0},"end":{"line":4,"column":36}},"4":{"start":{"line":5,"column":0},"end":{"line":5,"column":45}},"6":{"start":{"line":7,"column":0},"end":{"line":7,"column":73}},"7":{"start":{"line":8,"column":0},"end":{"line":8,"column":28}},"11":{"start":{"line":12,"column":0},"end":{"line":12,"column":46}},"36":{"start":{"line":37,"column":0},"end":{"line":37,"column":27}},"37":{"start":{"line":38,"column":0},"end":{"line":38,"column":36}},"38":{"start":{"line":39,"column":0},"end":{"line":39,"column":16}},"39":{"start":{"line":40,"column":0},"end":{"line":40,"column":21}},"40":{"start":{"line":41,"column":0},"end":{"line":41,"column":7}},"41":{"start":{"line":42,"column":0},"end":{"line":42,"column":51}},"42":{"start":{"line":43,"column":0},"end":{"line":43,"column":45}},"43":{"start":{"line":44,"column":0},"end":{"line":44,"column":16}},"44":{"start":{"line":45,"column":0},"end":{"line":45,"column":9}},"45":{"start":{"line":46,"column":0},"end":{"line":46,"column":22}},"46":{"start":{"line":47,"column":0},"end":{"line":47,"column":23}},"47":{"start":{"line":48,"column":0},"end":{"line":48,"column":17}},"48":{"start":{"line":49,"column":0},"end":{"line":49,"column":24}},"49":{"start":{"line":50,"column":0},"end":{"line":50,"column":23}},"50":{"start":{"line":51,"column":0},"end":{"line":51,"column":22}},"51":{"start":{"line":52,"column":0},"end":{"line":52,"column":27}},"52":{"start":{"line":53,"column":0},"end":{"line":53,"column":12}},"53":{"start":{"line":54,"column":0},"end":{"line":54,"column":10}},"54":{"start":{"line":55,"column":0},"end":{"line":55,"column":9}},"55":{"start":{"line":56,"column":0},"end":{"line":56,"column":25}},"56":{"start":{"line":57,"column":0},"end":{"line":57,"column":70}},"57":{"start":{"line":58,"column":0},"end":{"line":58,"column":10}},"58":{"start":{"line":59,"column":0},"end":{"line":59,"column":8}},"59":{"start":{"line":60,"column":0},"end":{"line":60,"column":7}},"61":{"start":{"line":62,"column":0},"end":{"line":62,"column":23}},"62":{"start":{"line":63,"column":0},"end":{"line":63,"column":65}},"63":{"start":{"line":64,"column":0},"end":{"line":64,"column":5}},"65":{"start":{"line":66,"column":0},"end":{"line":66,"column":26}},"66":{"start":{"line":67,"column":0},"end":{"line":67,"column":24}},"67":{"start":{"line":68,"column":0},"end":{"line":68,"column":35}},"68":{"start":{"line":69,"column":0},"end":{"line":69,"column":7}},"69":{"start":{"line":70,"column":0},"end":{"line":70,"column":19}},"70":{"start":{"line":71,"column":0},"end":{"line":71,"column":36}},"71":{"start":{"line":72,"column":0},"end":{"line":72,"column":68}},"72":{"start":{"line":73,"column":0},"end":{"line":73,"column":13}},"73":{"start":{"line":74,"column":0},"end":{"line":74,"column":5}},"74":{"start":{"line":75,"column":0},"end":{"line":75,"column":26}},"75":{"start":{"line":76,"column":0},"end":{"line":76,"column":14}},"76":{"start":{"line":77,"column":0},"end":{"line":77,"column":30}},"77":{"start":{"line":78,"column":0},"end":{"line":78,"column":25}},"78":{"start":{"line":79,"column":0},"end":{"line":79,"column":44}},"79":{"start":{"line":80,"column":0},"end":{"line":80,"column":7}},"80":{"start":{"line":81,"column":0},"end":{"line":81,"column":3}},"81":{"start":{"line":82,"column":0},"end":{"line":82,"column":2}},"84":{"start":{"line":85,"column":0},"end":{"line":85,"column":33}},"85":{"start":{"line":86,"column":0},"end":{"line":86,"column":77}},"86":{"start":{"line":87,"column":0},"end":{"line":87,"column":16}},"87":{"start":{"line":88,"column":0},"end":{"line":88,"column":21}},"88":{"start":{"line":89,"column":0},"end":{"line":89,"column":7}},"89":{"start":{"line":90,"column":0},"end":{"line":90,"column":44}},"90":{"start":{"line":91,"column":0},"end":{"line":91,"column":35}},"91":{"start":{"line":92,"column":0},"end":{"line":92,"column":6}},"93":{"start":{"line":94,"column":0},"end":{"line":94,"column":33}},"94":{"start":{"line":95,"column":0},"end":{"line":95,"column":25}},"95":{"start":{"line":96,"column":0},"end":{"line":96,"column":39}},"96":{"start":{"line":97,"column":0},"end":{"line":97,"column":63}},"97":{"start":{"line":98,"column":0},"end":{"line":98,"column":27}},"98":{"start":{"line":99,"column":0},"end":{"line":99,"column":58}},"99":{"start":{"line":100,"column":0},"end":{"line":100,"column":14}},"100":{"start":{"line":101,"column":0},"end":{"line":101,"column":10}},"101":{"start":{"line":102,"column":0},"end":{"line":102,"column":7}},"102":{"start":{"line":103,"column":0},"end":{"line":103,"column":28}},"103":{"start":{"line":104,"column":0},"end":{"line":104,"column":27}},"104":{"start":{"line":105,"column":0},"end":{"line":105,"column":8}},"105":{"start":{"line":106,"column":0},"end":{"line":106,"column":5}},"107":{"start":{"line":108,"column":0},"end":{"line":108,"column":55}},"108":{"start":{"line":109,"column":0},"end":{"line":109,"column":52}},"109":{"start":{"line":110,"column":0},"end":{"line":110,"column":16}},"110":{"start":{"line":111,"column":0},"end":{"line":111,"column":9}},"111":{"start":{"line":112,"column":0},"end":{"line":112,"column":22}},"112":{"start":{"line":113,"column":0},"end":{"line":113,"column":23}},"113":{"start":{"line":114,"column":0},"end":{"line":114,"column":17}},"114":{"start":{"line":115,"column":0},"end":{"line":115,"column":24}},"115":{"start":{"line":116,"column":0},"end":{"line":116,"column":23}},"116":{"start":{"line":117,"column":0},"end":{"line":117,"column":22}},"117":{"start":{"line":118,"column":0},"end":{"line":118,"column":27}},"118":{"start":{"line":119,"column":0},"end":{"line":119,"column":12}},"119":{"start":{"line":120,"column":0},"end":{"line":120,"column":10}},"120":{"start":{"line":121,"column":0},"end":{"line":121,"column":9}},"121":{"start":{"line":122,"column":0},"end":{"line":122,"column":25}},"122":{"start":{"line":123,"column":0},"end":{"line":123,"column":70}},"123":{"start":{"line":124,"column":0},"end":{"line":124,"column":10}},"124":{"start":{"line":125,"column":0},"end":{"line":125,"column":8}},"125":{"start":{"line":126,"column":0},"end":{"line":126,"column":7}},"127":{"start":{"line":128,"column":0},"end":{"line":128,"column":25}},"128":{"start":{"line":129,"column":0},"end":{"line":129,"column":28}},"129":{"start":{"line":130,"column":0},"end":{"line":130,"column":53}},"130":{"start":{"line":131,"column":0},"end":{"line":131,"column":16}},"131":{"start":{"line":132,"column":0},"end":{"line":132,"column":9}},"132":{"start":{"line":133,"column":0},"end":{"line":133,"column":13}},"133":{"start":{"line":134,"column":0},"end":{"line":134,"column":5}},"135":{"start":{"line":136,"column":0},"end":{"line":136,"column":38}},"136":{"start":{"line":137,"column":0},"end":{"line":137,"column":19}},"137":{"start":{"line":138,"column":0},"end":{"line":138,"column":36}},"138":{"start":{"line":139,"column":0},"end":{"line":139,"column":68}},"139":{"start":{"line":140,"column":0},"end":{"line":140,"column":13}},"140":{"start":{"line":141,"column":0},"end":{"line":141,"column":5}},"141":{"start":{"line":142,"column":0},"end":{"line":142,"column":26}},"142":{"start":{"line":143,"column":0},"end":{"line":143,"column":14}},"143":{"start":{"line":144,"column":0},"end":{"line":144,"column":30}},"144":{"start":{"line":145,"column":0},"end":{"line":145,"column":25}},"145":{"start":{"line":146,"column":0},"end":{"line":146,"column":47}},"146":{"start":{"line":147,"column":0},"end":{"line":147,"column":7}},"147":{"start":{"line":148,"column":0},"end":{"line":148,"column":3}},"148":{"start":{"line":149,"column":0},"end":{"line":149,"column":2}},"151":{"start":{"line":152,"column":0},"end":{"line":152,"column":28}},"152":{"start":{"line":153,"column":0},"end":{"line":153,"column":31}},"156":{"start":{"line":157,"column":0},"end":{"line":157,"column":16}},"157":{"start":{"line":158,"column":0},"end":{"line":158,"column":21}},"158":{"start":{"line":159,"column":0},"end":{"line":159,"column":7}},"159":{"start":{"line":160,"column":0},"end":{"line":160,"column":60}},"160":{"start":{"line":161,"column":0},"end":{"line":161,"column":62}},"161":{"start":{"line":162,"column":0},"end":{"line":162,"column":5}},"163":{"start":{"line":164,"column":0},"end":{"line":164,"column":41}},"166":{"start":{"line":167,"column":0},"end":{"line":167,"column":46}},"167":{"start":{"line":168,"column":0},"end":{"line":168,"column":64}},"168":{"start":{"line":169,"column":0},"end":{"line":169,"column":5}},"171":{"start":{"line":172,"column":0},"end":{"line":172,"column":27}},"172":{"start":{"line":173,"column":0},"end":{"line":173,"column":53}},"173":{"start":{"line":174,"column":0},"end":{"line":174,"column":5}},"175":{"start":{"line":176,"column":0},"end":{"line":176,"column":77}},"176":{"start":{"line":177,"column":0},"end":{"line":177,"column":17}},"177":{"start":{"line":178,"column":0},"end":{"line":178,"column":49}},"178":{"start":{"line":179,"column":0},"end":{"line":179,"column":5}},"180":{"start":{"line":181,"column":0},"end":{"line":181,"column":39}},"181":{"start":{"line":182,"column":0},"end":{"line":182,"column":25}},"182":{"start":{"line":183,"column":0},"end":{"line":183,"column":83}},"183":{"start":{"line":184,"column":0},"end":{"line":184,"column":12}},"184":{"start":{"line":185,"column":0},"end":{"line":185,"column":8}},"185":{"start":{"line":186,"column":0},"end":{"line":186,"column":5}},"187":{"start":{"line":188,"column":0},"end":{"line":188,"column":24}},"188":{"start":{"line":189,"column":0},"end":{"line":189,"column":35}},"189":{"start":{"line":190,"column":0},"end":{"line":190,"column":34}},"190":{"start":{"line":191,"column":0},"end":{"line":191,"column":34}},"191":{"start":{"line":192,"column":0},"end":{"line":192,"column":13}},"192":{"start":{"line":193,"column":0},"end":{"line":193,"column":14}},"193":{"start":{"line":194,"column":0},"end":{"line":194,"column":28}},"194":{"start":{"line":195,"column":0},"end":{"line":195,"column":28}},"195":{"start":{"line":196,"column":0},"end":{"line":196,"column":6}},"197":{"start":{"line":198,"column":0},"end":{"line":198,"column":44}},"199":{"start":{"line":200,"column":0},"end":{"line":200,"column":41}},"201":{"start":{"line":202,"column":0},"end":{"line":202,"column":23}},"202":{"start":{"line":203,"column":0},"end":{"line":203,"column":7}},"203":{"start":{"line":204,"column":0},"end":{"line":204,"column":28}},"204":{"start":{"line":205,"column":0},"end":{"line":205,"column":30}},"205":{"start":{"line":206,"column":0},"end":{"line":206,"column":8}},"206":{"start":{"line":207,"column":0},"end":{"line":207,"column":44}},"207":{"start":{"line":208,"column":0},"end":{"line":208,"column":6}},"209":{"start":{"line":210,"column":0},"end":{"line":210,"column":59}},"211":{"start":{"line":212,"column":0},"end":{"line":212,"column":36}},"212":{"start":{"line":213,"column":0},"end":{"line":213,"column":56}},"213":{"start":{"line":214,"column":0},"end":{"line":214,"column":32}},"214":{"start":{"line":215,"column":0},"end":{"line":215,"column":65}},"215":{"start":{"line":216,"column":0},"end":{"line":216,"column":8}},"217":{"start":{"line":218,"column":0},"end":{"line":218,"column":52}},"218":{"start":{"line":219,"column":0},"end":{"line":219,"column":41}},"219":{"start":{"line":220,"column":0},"end":{"line":220,"column":42}},"220":{"start":{"line":221,"column":0},"end":{"line":221,"column":8}},"222":{"start":{"line":223,"column":0},"end":{"line":223,"column":54}},"223":{"start":{"line":224,"column":0},"end":{"line":224,"column":33}},"224":{"start":{"line":225,"column":0},"end":{"line":225,"column":20}},"225":{"start":{"line":226,"column":0},"end":{"line":226,"column":8}},"227":{"start":{"line":228,"column":0},"end":{"line":228,"column":41}},"228":{"start":{"line":229,"column":0},"end":{"line":229,"column":33}},"229":{"start":{"line":230,"column":0},"end":{"line":230,"column":33}},"230":{"start":{"line":231,"column":0},"end":{"line":231,"column":25}},"231":{"start":{"line":232,"column":0},"end":{"line":232,"column":30}},"232":{"start":{"line":233,"column":0},"end":{"line":233,"column":10}},"233":{"start":{"line":234,"column":0},"end":{"line":234,"column":7}},"234":{"start":{"line":235,"column":0},"end":{"line":235,"column":5}},"236":{"start":{"line":237,"column":0},"end":{"line":237,"column":67}},"237":{"start":{"line":238,"column":0},"end":{"line":238,"column":19}},"238":{"start":{"line":239,"column":0},"end":{"line":239,"column":36}},"239":{"start":{"line":240,"column":0},"end":{"line":240,"column":68}},"240":{"start":{"line":241,"column":0},"end":{"line":241,"column":13}},"241":{"start":{"line":242,"column":0},"end":{"line":242,"column":5}},"242":{"start":{"line":243,"column":0},"end":{"line":243,"column":26}},"243":{"start":{"line":244,"column":0},"end":{"line":244,"column":80}},"244":{"start":{"line":245,"column":0},"end":{"line":245,"column":7}},"245":{"start":{"line":246,"column":0},"end":{"line":246,"column":3}},"246":{"start":{"line":247,"column":0},"end":{"line":247,"column":2}},"249":{"start":{"line":250,"column":0},"end":{"line":250,"column":28}},"250":{"start":{"line":251,"column":0},"end":{"line":251,"column":67}},"251":{"start":{"line":252,"column":0},"end":{"line":252,"column":16}},"252":{"start":{"line":253,"column":0},"end":{"line":253,"column":21}},"253":{"start":{"line":254,"column":0},"end":{"line":254,"column":7}},"254":{"start":{"line":255,"column":0},"end":{"line":255,"column":41}},"257":{"start":{"line":258,"column":0},"end":{"line":258,"column":61}},"258":{"start":{"line":259,"column":0},"end":{"line":259,"column":64}},"259":{"start":{"line":260,"column":0},"end":{"line":260,"column":5}},"262":{"start":{"line":263,"column":0},"end":{"line":263,"column":51}},"263":{"start":{"line":264,"column":0},"end":{"line":264,"column":57}},"264":{"start":{"line":265,"column":0},"end":{"line":265,"column":5}},"266":{"start":{"line":267,"column":0},"end":{"line":267,"column":67}},"267":{"start":{"line":268,"column":0},"end":{"line":268,"column":18}},"268":{"start":{"line":269,"column":0},"end":{"line":269,"column":50}},"269":{"start":{"line":270,"column":0},"end":{"line":270,"column":5}},"271":{"start":{"line":272,"column":0},"end":{"line":272,"column":52}},"272":{"start":{"line":273,"column":0},"end":{"line":273,"column":7}},"273":{"start":{"line":274,"column":0},"end":{"line":274,"column":20}},"274":{"start":{"line":275,"column":0},"end":{"line":275,"column":30}},"275":{"start":{"line":276,"column":0},"end":{"line":276,"column":8}},"276":{"start":{"line":277,"column":0},"end":{"line":277,"column":45}},"277":{"start":{"line":278,"column":0},"end":{"line":278,"column":6}},"279":{"start":{"line":280,"column":0},"end":{"line":280,"column":29}},"280":{"start":{"line":281,"column":0},"end":{"line":281,"column":57}},"281":{"start":{"line":282,"column":0},"end":{"line":282,"column":5}},"283":{"start":{"line":284,"column":0},"end":{"line":284,"column":69}},"284":{"start":{"line":285,"column":0},"end":{"line":285,"column":19}},"285":{"start":{"line":286,"column":0},"end":{"line":286,"column":36}},"286":{"start":{"line":287,"column":0},"end":{"line":287,"column":68}},"287":{"start":{"line":288,"column":0},"end":{"line":288,"column":13}},"288":{"start":{"line":289,"column":0},"end":{"line":289,"column":5}},"289":{"start":{"line":290,"column":0},"end":{"line":290,"column":26}},"290":{"start":{"line":291,"column":0},"end":{"line":291,"column":80}},"291":{"start":{"line":292,"column":0},"end":{"line":292,"column":7}},"292":{"start":{"line":293,"column":0},"end":{"line":293,"column":3}},"293":{"start":{"line":294,"column":0},"end":{"line":294,"column":2}},"296":{"start":{"line":297,"column":0},"end":{"line":297,"column":31}},"297":{"start":{"line":298,"column":0},"end":{"line":298,"column":63}},"298":{"start":{"line":299,"column":0},"end":{"line":299,"column":16}},"299":{"start":{"line":300,"column":0},"end":{"line":300,"column":21}},"300":{"start":{"line":301,"column":0},"end":{"line":301,"column":7}},"301":{"start":{"line":302,"column":0},"end":{"line":302,"column":60}},"302":{"start":{"line":303,"column":0},"end":{"line":303,"column":62}},"303":{"start":{"line":304,"column":0},"end":{"line":304,"column":5}},"305":{"start":{"line":306,"column":0},"end":{"line":306,"column":46}},"306":{"start":{"line":307,"column":0},"end":{"line":307,"column":41}},"307":{"start":{"line":308,"column":0},"end":{"line":308,"column":16}},"308":{"start":{"line":309,"column":0},"end":{"line":309,"column":9}},"309":{"start":{"line":310,"column":0},"end":{"line":310,"column":22}},"310":{"start":{"line":311,"column":0},"end":{"line":311,"column":23}},"311":{"start":{"line":312,"column":0},"end":{"line":312,"column":17}},"312":{"start":{"line":313,"column":0},"end":{"line":313,"column":24}},"313":{"start":{"line":314,"column":0},"end":{"line":314,"column":23}},"314":{"start":{"line":315,"column":0},"end":{"line":315,"column":22}},"315":{"start":{"line":316,"column":0},"end":{"line":316,"column":27}},"316":{"start":{"line":317,"column":0},"end":{"line":317,"column":12}},"317":{"start":{"line":318,"column":0},"end":{"line":318,"column":10}},"318":{"start":{"line":319,"column":0},"end":{"line":319,"column":8}},"319":{"start":{"line":320,"column":0},"end":{"line":320,"column":7}},"321":{"start":{"line":322,"column":0},"end":{"line":322,"column":18}},"322":{"start":{"line":323,"column":0},"end":{"line":323,"column":50}},"323":{"start":{"line":324,"column":0},"end":{"line":324,"column":5}},"325":{"start":{"line":326,"column":0},"end":{"line":326,"column":48}},"326":{"start":{"line":327,"column":0},"end":{"line":327,"column":78}},"327":{"start":{"line":328,"column":0},"end":{"line":328,"column":5}},"329":{"start":{"line":330,"column":0},"end":{"line":330,"column":52}},"330":{"start":{"line":331,"column":0},"end":{"line":331,"column":41}},"331":{"start":{"line":332,"column":0},"end":{"line":332,"column":7}},"333":{"start":{"line":334,"column":0},"end":{"line":334,"column":29}},"334":{"start":{"line":335,"column":0},"end":{"line":335,"column":57}},"335":{"start":{"line":336,"column":0},"end":{"line":336,"column":5}},"337":{"start":{"line":338,"column":0},"end":{"line":338,"column":26}},"338":{"start":{"line":339,"column":0},"end":{"line":339,"column":19}},"339":{"start":{"line":340,"column":0},"end":{"line":340,"column":36}},"340":{"start":{"line":341,"column":0},"end":{"line":341,"column":68}},"341":{"start":{"line":342,"column":0},"end":{"line":342,"column":13}},"342":{"start":{"line":343,"column":0},"end":{"line":343,"column":5}},"343":{"start":{"line":344,"column":0},"end":{"line":344,"column":26}},"344":{"start":{"line":345,"column":0},"end":{"line":345,"column":80}},"345":{"start":{"line":346,"column":0},"end":{"line":346,"column":7}},"346":{"start":{"line":347,"column":0},"end":{"line":347,"column":3}},"347":{"start":{"line":348,"column":0},"end":{"line":348,"column":2}}},"s":{"0":1,"1":1,"2":1,"3":1,"4":1,"6":1,"7":1,"11":1,"36":1,"37":0,"38":0,"39":0,"40":0,"41":0,"42":0,"43":0,"44":0,"45":0,"46":0,"47":0,"48":0,"49":0,"50":0,"51":0,"52":0,"53":0,"54":0,"55":0,"56":0,"57":0,"58":0,"59":0,"61":0,"62":0,"63":0,"65":0,"66":0,"67":0,"68":0,"69":0,"70":0,"71":0,"72":0,"73":0,"74":0,"75":0,"76":0,"77":0,"78":0,"79":0,"80":0,"81":0,"84":1,"85":0,"86":0,"87":0,"88":0,"89":0,"90":0,"91":0,"93":0,"94":0,"95":0,"96":0,"97":0,"98":0,"99":0,"100":0,"101":0,"102":0,"103":0,"104":0,"105":0,"107":0,"108":0,"109":0,"110":0,"111":0,"112":0,"113":0,"114":0,"115":0,"116":0,"117":0,"118":0,"119":0,"120":0,"121":0,"122":0,"123":0,"124":0,"125":0,"127":0,"128":0,"129":0,"130":0,"131":0,"132":0,"133":0,"135":0,"136":0,"137":0,"138":0,"139":0,"140":0,"141":0,"142":0,"143":0,"144":0,"145":0,"146":0,"147":0,"148":0,"151":1,"152":0,"156":0,"157":0,"158":0,"159":0,"160":0,"161":0,"163":0,"166":0,"167":0,"168":0,"171":0,"172":0,"173":0,"175":0,"176":0,"177":0,"178":0,"180":0,"181":0,"182":0,"183":0,"184":0,"185":0,"187":0,"188":0,"189":0,"190":0,"191":0,"192":0,"193":0,"194":0,"195":0,"197":0,"199":0,"201":0,"202":0,"203":0,"204":0,"205":0,"206":0,"207":0,"209":0,"211":0,"212":0,"213":0,"214":0,"215":0,"217":0,"218":0,"219":0,"220":0,"222":0,"223":0,"224":0,"225":0,"227":0,"228":0,"229":0,"230":0,"231":0,"232":0,"233":0,"234":0,"236":0,"237":0,"238":0,"239":0,"240":0,"241":0,"242":0,"243":0,"244":0,"245":0,"246":0,"249":1,"250":0,"251":0,"252":0,"253":0,"254":0,"257":0,"258":0,"259":0,"262":0,"263":0,"264":0,"266":0,"267":0,"268":0,"269":0,"271":0,"272":0,"273":0,"274":0,"275":0,"276":0,"277":0,"279":0,"280":0,"281":0,"283":0,"284":0,"285":0,"286":0,"287":0,"288":0,"289":0,"290":0,"291":0,"292":0,"293":0,"296":1,"297":0,"298":0,"299":0,"300":0,"301":0,"302":0,"303":0,"305":0,"306":0,"307":0,"308":0,"309":0,"310":0,"311":0,"312":0,"313":0,"314":0,"315":0,"316":0,"317":0,"318":0,"319":0,"321":0,"322":0,"323":0,"325":0,"326":0,"327":0,"329":0,"330":0,"331":0,"333":0,"334":0,"335":0,"337":0,"338":0,"339":0,"340":0,"341":0,"342":0,"343":0,"344":0,"345":0,"346":0,"347":0},"branchMap":{},"b":{},"fnMap":{"0":{"name":"orderReview","decl":{"start":{"line":37,"column":20},"end":{"line":82,"column":2}},"loc":{"start":{"line":37,"column":20},"end":{"line":82,"column":2}},"line":37},"1":{"name":"getReviewByProdId","decl":{"start":{"line":85,"column":26},"end":{"line":149,"column":2}},"loc":{"start":{"line":85,"column":26},"end":{"line":149,"column":2}},"line":85},"2":{"name":"createReview","decl":{"start":{"line":152,"column":21},"end":{"line":247,"column":2}},"loc":{"start":{"line":152,"column":21},"end":{"line":247,"column":2}},"line":152},"3":{"name":"updateReview","decl":{"start":{"line":250,"column":21},"end":{"line":294,"column":2}},"loc":{"start":{"line":250,"column":21},"end":{"line":294,"column":2}},"line":250},"4":{"name":"deleteOwnReview","decl":{"start":{"line":297,"column":24},"end":{"line":348,"column":2}},"loc":{"start":{"line":297,"column":24},"end":{"line":348,"column":2}},"line":297}},"f":{"0":0,"1":0,"2":0,"3":0,"4":0}} +,"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/src/controllers/user.controller.ts": {"path":"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/src/controllers/user.controller.ts","all":false,"statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":34}},"1":{"start":{"line":2,"column":0},"end":{"line":2,"column":45}},"2":{"start":{"line":3,"column":0},"end":{"line":3,"column":46}},"17":{"start":{"line":18,"column":0},"end":{"line":18,"column":19}},"18":{"start":{"line":19,"column":0},"end":{"line":19,"column":48}},"19":{"start":{"line":20,"column":0},"end":{"line":20,"column":42}},"20":{"start":{"line":21,"column":0},"end":{"line":21,"column":48}},"21":{"start":{"line":22,"column":0},"end":{"line":22,"column":3}},"24":{"start":{"line":25,"column":0},"end":{"line":25,"column":37}},"25":{"start":{"line":26,"column":0},"end":{"line":26,"column":15}},"26":{"start":{"line":27,"column":0},"end":{"line":27,"column":16}},"27":{"start":{"line":28,"column":0},"end":{"line":28,"column":21}},"28":{"start":{"line":29,"column":0},"end":{"line":29,"column":7}},"29":{"start":{"line":30,"column":0},"end":{"line":30,"column":39}},"30":{"start":{"line":31,"column":0},"end":{"line":31,"column":25}},"31":{"start":{"line":32,"column":0},"end":{"line":32,"column":58}},"32":{"start":{"line":33,"column":0},"end":{"line":33,"column":13}},"33":{"start":{"line":34,"column":0},"end":{"line":34,"column":5}},"36":{"start":{"line":37,"column":0},"end":{"line":37,"column":54}},"38":{"start":{"line":39,"column":0},"end":{"line":39,"column":54}},"39":{"start":{"line":40,"column":0},"end":{"line":40,"column":22}},"40":{"start":{"line":41,"column":0},"end":{"line":41,"column":7}},"42":{"start":{"line":43,"column":0},"end":{"line":43,"column":47}},"43":{"start":{"line":44,"column":0},"end":{"line":44,"column":19}},"44":{"start":{"line":45,"column":0},"end":{"line":45,"column":26}},"45":{"start":{"line":46,"column":0},"end":{"line":46,"column":14}},"46":{"start":{"line":47,"column":0},"end":{"line":47,"column":74}},"47":{"start":{"line":48,"column":0},"end":{"line":48,"column":7}},"48":{"start":{"line":49,"column":0},"end":{"line":49,"column":3}},"49":{"start":{"line":50,"column":0},"end":{"line":50,"column":2}},"52":{"start":{"line":53,"column":0},"end":{"line":53,"column":34}},"53":{"start":{"line":54,"column":0},"end":{"line":54,"column":15}},"54":{"start":{"line":55,"column":0},"end":{"line":55,"column":16}},"55":{"start":{"line":56,"column":0},"end":{"line":56,"column":21}},"56":{"start":{"line":57,"column":0},"end":{"line":57,"column":31}},"58":{"start":{"line":59,"column":0},"end":{"line":59,"column":7}},"59":{"start":{"line":60,"column":0},"end":{"line":60,"column":55}},"60":{"start":{"line":61,"column":0},"end":{"line":61,"column":16}},"61":{"start":{"line":62,"column":0},"end":{"line":62,"column":62}},"62":{"start":{"line":63,"column":0},"end":{"line":63,"column":13}},"63":{"start":{"line":64,"column":0},"end":{"line":64,"column":5}},"66":{"start":{"line":67,"column":0},"end":{"line":67,"column":52}},"68":{"start":{"line":69,"column":0},"end":{"line":69,"column":35}},"69":{"start":{"line":70,"column":0},"end":{"line":70,"column":19}},"70":{"start":{"line":71,"column":0},"end":{"line":71,"column":26}},"71":{"start":{"line":72,"column":0},"end":{"line":72,"column":80}},"72":{"start":{"line":73,"column":0},"end":{"line":73,"column":7}},"73":{"start":{"line":74,"column":0},"end":{"line":74,"column":3}},"74":{"start":{"line":75,"column":0},"end":{"line":75,"column":2}},"77":{"start":{"line":78,"column":0},"end":{"line":78,"column":33}},"78":{"start":{"line":79,"column":0},"end":{"line":79,"column":28}},"79":{"start":{"line":80,"column":0},"end":{"line":80,"column":16}},"80":{"start":{"line":81,"column":0},"end":{"line":81,"column":21}},"81":{"start":{"line":82,"column":0},"end":{"line":82,"column":21}},"82":{"start":{"line":83,"column":0},"end":{"line":83,"column":35}},"84":{"start":{"line":85,"column":0},"end":{"line":85,"column":7}},"85":{"start":{"line":86,"column":0},"end":{"line":86,"column":45}},"86":{"start":{"line":87,"column":0},"end":{"line":87,"column":16}},"87":{"start":{"line":88,"column":0},"end":{"line":88,"column":58}},"88":{"start":{"line":89,"column":0},"end":{"line":89,"column":13}},"89":{"start":{"line":90,"column":0},"end":{"line":90,"column":5}},"91":{"start":{"line":92,"column":0},"end":{"line":92,"column":19}},"92":{"start":{"line":93,"column":0},"end":{"line":93,"column":67}},"93":{"start":{"line":94,"column":0},"end":{"line":94,"column":22}},"94":{"start":{"line":95,"column":0},"end":{"line":95,"column":31}},"95":{"start":{"line":96,"column":0},"end":{"line":96,"column":56}},"96":{"start":{"line":97,"column":0},"end":{"line":97,"column":5}},"98":{"start":{"line":99,"column":0},"end":{"line":99,"column":29}},"99":{"start":{"line":100,"column":0},"end":{"line":100,"column":18}},"100":{"start":{"line":101,"column":0},"end":{"line":101,"column":28}},"101":{"start":{"line":102,"column":0},"end":{"line":102,"column":6}},"103":{"start":{"line":104,"column":0},"end":{"line":104,"column":49}},"104":{"start":{"line":105,"column":0},"end":{"line":105,"column":42}},"105":{"start":{"line":106,"column":0},"end":{"line":106,"column":32}},"106":{"start":{"line":107,"column":0},"end":{"line":107,"column":44}},"107":{"start":{"line":108,"column":0},"end":{"line":108,"column":48}},"108":{"start":{"line":109,"column":0},"end":{"line":109,"column":54}},"110":{"start":{"line":111,"column":0},"end":{"line":111,"column":80}},"112":{"start":{"line":113,"column":0},"end":{"line":113,"column":49}},"113":{"start":{"line":114,"column":0},"end":{"line":114,"column":49}},"114":{"start":{"line":115,"column":0},"end":{"line":115,"column":8}},"116":{"start":{"line":117,"column":0},"end":{"line":117,"column":32}},"117":{"start":{"line":118,"column":0},"end":{"line":118,"column":61}},"118":{"start":{"line":119,"column":0},"end":{"line":119,"column":50}},"119":{"start":{"line":120,"column":0},"end":{"line":120,"column":7}},"120":{"start":{"line":121,"column":0},"end":{"line":121,"column":5}},"122":{"start":{"line":123,"column":0},"end":{"line":123,"column":35}},"123":{"start":{"line":124,"column":0},"end":{"line":124,"column":72}},"124":{"start":{"line":125,"column":0},"end":{"line":125,"column":5}},"126":{"start":{"line":127,"column":0},"end":{"line":127,"column":66}},"128":{"start":{"line":129,"column":0},"end":{"line":129,"column":53}},"129":{"start":{"line":130,"column":0},"end":{"line":130,"column":44}},"130":{"start":{"line":131,"column":0},"end":{"line":131,"column":7}},"132":{"start":{"line":133,"column":0},"end":{"line":133,"column":26}},"133":{"start":{"line":134,"column":0},"end":{"line":134,"column":46}},"134":{"start":{"line":135,"column":0},"end":{"line":135,"column":28}},"135":{"start":{"line":136,"column":0},"end":{"line":136,"column":7}},"136":{"start":{"line":137,"column":0},"end":{"line":137,"column":19}},"137":{"start":{"line":138,"column":0},"end":{"line":138,"column":16}},"138":{"start":{"line":139,"column":0},"end":{"line":139,"column":3}},"139":{"start":{"line":140,"column":0},"end":{"line":140,"column":2}},"142":{"start":{"line":143,"column":0},"end":{"line":143,"column":33}},"143":{"start":{"line":144,"column":0},"end":{"line":144,"column":15}},"144":{"start":{"line":145,"column":0},"end":{"line":145,"column":16}},"145":{"start":{"line":146,"column":0},"end":{"line":146,"column":21}},"146":{"start":{"line":147,"column":0},"end":{"line":147,"column":7}},"147":{"start":{"line":148,"column":0},"end":{"line":148,"column":37}},"149":{"start":{"line":150,"column":0},"end":{"line":150,"column":71}},"150":{"start":{"line":151,"column":0},"end":{"line":151,"column":24}},"151":{"start":{"line":152,"column":0},"end":{"line":152,"column":62}},"152":{"start":{"line":153,"column":0},"end":{"line":153,"column":13}},"153":{"start":{"line":154,"column":0},"end":{"line":154,"column":5}},"155":{"start":{"line":156,"column":0},"end":{"line":156,"column":50}},"156":{"start":{"line":157,"column":0},"end":{"line":157,"column":67}},"157":{"start":{"line":158,"column":0},"end":{"line":158,"column":19}},"158":{"start":{"line":159,"column":0},"end":{"line":159,"column":26}},"159":{"start":{"line":160,"column":0},"end":{"line":160,"column":78}},"160":{"start":{"line":161,"column":0},"end":{"line":161,"column":7}},"161":{"start":{"line":162,"column":0},"end":{"line":162,"column":3}},"162":{"start":{"line":163,"column":0},"end":{"line":163,"column":2}},"165":{"start":{"line":166,"column":0},"end":{"line":166,"column":37}},"166":{"start":{"line":167,"column":0},"end":{"line":167,"column":15}},"167":{"start":{"line":168,"column":0},"end":{"line":168,"column":16}},"168":{"start":{"line":169,"column":0},"end":{"line":169,"column":21}},"169":{"start":{"line":170,"column":0},"end":{"line":170,"column":21}},"170":{"start":{"line":171,"column":0},"end":{"line":171,"column":53}},"172":{"start":{"line":173,"column":0},"end":{"line":173,"column":18}},"173":{"start":{"line":174,"column":0},"end":{"line":174,"column":26}},"174":{"start":{"line":175,"column":0},"end":{"line":175,"column":23}},"175":{"start":{"line":176,"column":0},"end":{"line":176,"column":36}},"176":{"start":{"line":177,"column":0},"end":{"line":177,"column":7}},"177":{"start":{"line":178,"column":0},"end":{"line":178,"column":11}},"178":{"start":{"line":179,"column":0},"end":{"line":179,"column":3}},"180":{"start":{"line":181,"column":0},"end":{"line":181,"column":28}},"181":{"start":{"line":182,"column":0},"end":{"line":182,"column":26}},"182":{"start":{"line":183,"column":0},"end":{"line":183,"column":23}},"183":{"start":{"line":184,"column":0},"end":{"line":184,"column":56}},"184":{"start":{"line":185,"column":0},"end":{"line":185,"column":7}},"185":{"start":{"line":186,"column":0},"end":{"line":186,"column":11}},"186":{"start":{"line":187,"column":0},"end":{"line":187,"column":3}},"188":{"start":{"line":189,"column":0},"end":{"line":189,"column":7}},"189":{"start":{"line":190,"column":0},"end":{"line":190,"column":67}},"190":{"start":{"line":191,"column":0},"end":{"line":191,"column":20}},"191":{"start":{"line":192,"column":0},"end":{"line":192,"column":58}},"192":{"start":{"line":193,"column":0},"end":{"line":193,"column":13}},"193":{"start":{"line":194,"column":0},"end":{"line":194,"column":5}},"195":{"start":{"line":196,"column":0},"end":{"line":196,"column":22}},"196":{"start":{"line":197,"column":0},"end":{"line":197,"column":43}},"197":{"start":{"line":198,"column":0},"end":{"line":198,"column":20}},"198":{"start":{"line":199,"column":0},"end":{"line":199,"column":36}},"199":{"start":{"line":200,"column":0},"end":{"line":200,"column":8}},"200":{"start":{"line":201,"column":0},"end":{"line":201,"column":28}},"201":{"start":{"line":202,"column":0},"end":{"line":202,"column":30}},"202":{"start":{"line":203,"column":0},"end":{"line":203,"column":18}},"203":{"start":{"line":204,"column":0},"end":{"line":204,"column":87}},"204":{"start":{"line":205,"column":0},"end":{"line":205,"column":11}},"205":{"start":{"line":206,"column":0},"end":{"line":206,"column":15}},"206":{"start":{"line":207,"column":0},"end":{"line":207,"column":7}},"207":{"start":{"line":208,"column":0},"end":{"line":208,"column":5}},"209":{"start":{"line":210,"column":0},"end":{"line":210,"column":50}},"210":{"start":{"line":211,"column":0},"end":{"line":211,"column":39}},"211":{"start":{"line":212,"column":0},"end":{"line":212,"column":36}},"213":{"start":{"line":214,"column":0},"end":{"line":214,"column":59}},"214":{"start":{"line":215,"column":0},"end":{"line":215,"column":71}},"215":{"start":{"line":216,"column":0},"end":{"line":216,"column":19}},"216":{"start":{"line":217,"column":0},"end":{"line":217,"column":16}},"217":{"start":{"line":218,"column":0},"end":{"line":218,"column":3}},"218":{"start":{"line":219,"column":0},"end":{"line":219,"column":2}},"220":{"start":{"line":221,"column":0},"end":{"line":221,"column":40}},"221":{"start":{"line":222,"column":0},"end":{"line":222,"column":15}},"222":{"start":{"line":223,"column":0},"end":{"line":223,"column":16}},"223":{"start":{"line":224,"column":0},"end":{"line":224,"column":21}},"224":{"start":{"line":225,"column":0},"end":{"line":225,"column":21}},"225":{"start":{"line":226,"column":0},"end":{"line":226,"column":32}},"227":{"start":{"line":228,"column":0},"end":{"line":228,"column":7}},"228":{"start":{"line":229,"column":0},"end":{"line":229,"column":22}},"229":{"start":{"line":230,"column":0},"end":{"line":230,"column":7}},"230":{"start":{"line":231,"column":0},"end":{"line":231,"column":30}},"231":{"start":{"line":232,"column":0},"end":{"line":232,"column":30}},"232":{"start":{"line":233,"column":0},"end":{"line":233,"column":8}},"233":{"start":{"line":234,"column":0},"end":{"line":234,"column":32}},"234":{"start":{"line":235,"column":0},"end":{"line":235,"column":6}},"236":{"start":{"line":237,"column":0},"end":{"line":237,"column":67}},"238":{"start":{"line":239,"column":0},"end":{"line":239,"column":26}},"239":{"start":{"line":240,"column":0},"end":{"line":240,"column":55}},"240":{"start":{"line":241,"column":0},"end":{"line":241,"column":21}},"241":{"start":{"line":242,"column":0},"end":{"line":242,"column":7}},"242":{"start":{"line":243,"column":0},"end":{"line":243,"column":19}},"243":{"start":{"line":244,"column":0},"end":{"line":244,"column":16}},"244":{"start":{"line":245,"column":0},"end":{"line":245,"column":3}},"245":{"start":{"line":246,"column":0},"end":{"line":246,"column":2}},"247":{"start":{"line":248,"column":0},"end":{"line":248,"column":51}},"248":{"start":{"line":249,"column":0},"end":{"line":249,"column":15}},"249":{"start":{"line":250,"column":0},"end":{"line":250,"column":16}},"250":{"start":{"line":251,"column":0},"end":{"line":251,"column":21}},"251":{"start":{"line":252,"column":0},"end":{"line":252,"column":21}},"252":{"start":{"line":253,"column":0},"end":{"line":253,"column":32}},"253":{"start":{"line":254,"column":0},"end":{"line":254,"column":37}},"255":{"start":{"line":256,"column":0},"end":{"line":256,"column":7}},"256":{"start":{"line":257,"column":0},"end":{"line":257,"column":22}},"257":{"start":{"line":258,"column":0},"end":{"line":258,"column":7}},"258":{"start":{"line":259,"column":0},"end":{"line":259,"column":22}},"259":{"start":{"line":260,"column":0},"end":{"line":260,"column":30}},"260":{"start":{"line":261,"column":0},"end":{"line":261,"column":8}},"261":{"start":{"line":262,"column":0},"end":{"line":262,"column":32}},"262":{"start":{"line":263,"column":0},"end":{"line":263,"column":6}},"264":{"start":{"line":265,"column":0},"end":{"line":265,"column":71}},"265":{"start":{"line":266,"column":0},"end":{"line":266,"column":19}},"266":{"start":{"line":267,"column":0},"end":{"line":267,"column":16}},"267":{"start":{"line":268,"column":0},"end":{"line":268,"column":3}},"268":{"start":{"line":269,"column":0},"end":{"line":269,"column":2}}},"s":{"0":1,"1":1,"2":1,"17":1,"18":1,"19":1,"20":1,"21":1,"24":1,"25":0,"26":0,"27":0,"28":0,"29":0,"30":0,"31":0,"32":0,"33":0,"36":0,"38":0,"39":0,"40":0,"42":0,"43":0,"44":0,"45":0,"46":0,"47":0,"48":0,"49":0,"52":1,"53":0,"54":0,"55":0,"56":0,"58":0,"59":0,"60":0,"61":0,"62":0,"63":0,"66":0,"68":0,"69":0,"70":0,"71":0,"72":0,"73":0,"74":0,"77":1,"78":0,"79":0,"80":0,"81":0,"82":0,"84":0,"85":0,"86":0,"87":0,"88":0,"89":0,"91":0,"92":0,"93":0,"94":0,"95":0,"96":0,"98":0,"99":0,"100":0,"101":0,"103":0,"104":0,"105":0,"106":0,"107":0,"108":0,"110":0,"112":0,"113":0,"114":0,"116":0,"117":0,"118":0,"119":0,"120":0,"122":0,"123":0,"124":0,"126":0,"128":0,"129":0,"130":0,"132":0,"133":0,"134":0,"135":0,"136":0,"137":0,"138":0,"139":0,"142":1,"143":0,"144":0,"145":0,"146":0,"147":0,"149":0,"150":0,"151":0,"152":0,"153":0,"155":0,"156":0,"157":0,"158":0,"159":0,"160":0,"161":0,"162":0,"165":1,"166":0,"167":0,"168":0,"169":0,"170":0,"172":0,"173":0,"174":0,"175":0,"176":0,"177":0,"178":0,"180":0,"181":0,"182":0,"183":0,"184":0,"185":0,"186":0,"188":0,"189":0,"190":0,"191":0,"192":0,"193":0,"195":0,"196":0,"197":0,"198":0,"199":0,"200":0,"201":0,"202":0,"203":0,"204":0,"205":0,"206":0,"207":0,"209":0,"210":0,"211":0,"213":0,"214":0,"215":0,"216":0,"217":0,"218":0,"220":1,"221":0,"222":0,"223":0,"224":0,"225":0,"227":0,"228":0,"229":0,"230":0,"231":0,"232":0,"233":0,"234":0,"236":0,"238":0,"239":0,"240":0,"241":0,"242":0,"243":0,"244":0,"245":0,"247":1,"248":0,"249":0,"250":0,"251":0,"252":0,"253":0,"255":0,"256":0,"257":0,"258":0,"259":0,"260":0,"261":0,"262":0,"264":0,"265":0,"266":0,"267":0,"268":0},"branchMap":{},"b":{},"fnMap":{"0":{"name":"getAllUserData","decl":{"start":{"line":25,"column":30},"end":{"line":50,"column":2}},"loc":{"start":{"line":25,"column":30},"end":{"line":50,"column":2}},"line":25},"1":{"name":"getUserData","decl":{"start":{"line":53,"column":27},"end":{"line":75,"column":2}},"loc":{"start":{"line":53,"column":27},"end":{"line":75,"column":2}},"line":53},"2":{"name":"updateUser","decl":{"start":{"line":78,"column":26},"end":{"line":140,"column":2}},"loc":{"start":{"line":78,"column":26},"end":{"line":140,"column":2}},"line":78},"3":{"name":"deleteUser","decl":{"start":{"line":143,"column":26},"end":{"line":163,"column":2}},"loc":{"start":{"line":143,"column":26},"end":{"line":163,"column":2}},"line":143},"4":{"name":"updatePassword","decl":{"start":{"line":166,"column":30},"end":{"line":219,"column":2}},"loc":{"start":{"line":166,"column":30},"end":{"line":219,"column":2}},"line":166},"5":{"name":"updateShipAddress","decl":{"start":{"line":221,"column":33},"end":{"line":246,"column":2}},"loc":{"start":{"line":221,"column":33},"end":{"line":246,"column":2}},"line":221},"6":{"name":"addExpoPushNotificationToken","decl":{"start":{"line":248,"column":44},"end":{"line":269,"column":2}},"loc":{"start":{"line":248,"column":44},"end":{"line":269,"column":2}},"line":248}},"f":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0}} +,"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/src/errors/customErrors.ts": {"path":"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/src/errors/customErrors.ts","all":false,"statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":30}},"5":{"start":{"line":6,"column":0},"end":{"line":6,"column":52}},"6":{"start":{"line":7,"column":0},"end":{"line":7,"column":19}},"8":{"start":{"line":9,"column":0},"end":{"line":9,"column":33}},"9":{"start":{"line":10,"column":0},"end":{"line":10,"column":69}},"10":{"start":{"line":11,"column":0},"end":{"line":11,"column":30}},"12":{"start":{"line":13,"column":0},"end":{"line":13,"column":52}},"13":{"start":{"line":14,"column":0},"end":{"line":14,"column":3}},"14":{"start":{"line":15,"column":0},"end":{"line":15,"column":1}},"16":{"start":{"line":17,"column":0},"end":{"line":17,"column":24}}},"s":{"0":1,"5":1,"6":5,"8":5,"9":5,"10":5,"12":5,"13":5,"14":1,"16":1},"branchMap":{"0":{"type":"branch","line":6,"loc":{"start":{"line":6,"column":2},"end":{"line":14,"column":3}},"locations":[{"start":{"line":6,"column":2},"end":{"line":14,"column":3}}]},"1":{"type":"branch","line":10,"loc":{"start":{"line":10,"column":48},"end":{"line":10,"column":61}},"locations":[{"start":{"line":10,"column":48},"end":{"line":10,"column":61}}]},"2":{"type":"branch","line":10,"loc":{"start":{"line":10,"column":52},"end":{"line":10,"column":69}},"locations":[{"start":{"line":10,"column":52},"end":{"line":10,"column":69}}]}},"b":{"0":[5],"1":[3],"2":[2]},"fnMap":{"0":{"name":"AppError","decl":{"start":{"line":6,"column":2},"end":{"line":14,"column":3}},"loc":{"start":{"line":6,"column":2},"end":{"line":14,"column":3}},"line":6}},"f":{"0":5}} +,"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/src/middleware/auth-check.ts": {"path":"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/src/middleware/auth-check.ts","all":false,"statementMap":{"1":{"start":{"line":2,"column":0},"end":{"line":2,"column":13}},"6":{"start":{"line":7,"column":0},"end":{"line":7,"column":46}},"16":{"start":{"line":17,"column":0},"end":{"line":17,"column":25}},"17":{"start":{"line":18,"column":0},"end":{"line":18,"column":28}},"18":{"start":{"line":19,"column":0},"end":{"line":19,"column":16}},"19":{"start":{"line":20,"column":0},"end":{"line":20,"column":21}},"20":{"start":{"line":21,"column":0},"end":{"line":21,"column":21}},"21":{"start":{"line":22,"column":0},"end":{"line":22,"column":7}},"23":{"start":{"line":24,"column":0},"end":{"line":24,"column":49}},"24":{"start":{"line":25,"column":0},"end":{"line":25,"column":22}},"25":{"start":{"line":26,"column":0},"end":{"line":26,"column":28}},"26":{"start":{"line":27,"column":0},"end":{"line":27,"column":76}},"27":{"start":{"line":28,"column":0},"end":{"line":28,"column":9}},"28":{"start":{"line":29,"column":0},"end":{"line":29,"column":13}},"29":{"start":{"line":30,"column":0},"end":{"line":30,"column":5}},"32":{"start":{"line":33,"column":0},"end":{"line":33,"column":50}},"33":{"start":{"line":34,"column":0},"end":{"line":34,"column":40}},"34":{"start":{"line":35,"column":0},"end":{"line":35,"column":28}},"35":{"start":{"line":36,"column":0},"end":{"line":36,"column":55}},"36":{"start":{"line":37,"column":0},"end":{"line":37,"column":9}},"37":{"start":{"line":38,"column":0},"end":{"line":38,"column":13}},"38":{"start":{"line":39,"column":0},"end":{"line":39,"column":5}},"41":{"start":{"line":42,"column":0},"end":{"line":42,"column":36}},"42":{"start":{"line":43,"column":0},"end":{"line":43,"column":12}},"43":{"start":{"line":44,"column":0},"end":{"line":44,"column":39}},"44":{"start":{"line":45,"column":0},"end":{"line":45,"column":22}},"47":{"start":{"line":48,"column":0},"end":{"line":48,"column":67}},"48":{"start":{"line":49,"column":0},"end":{"line":49,"column":28}},"49":{"start":{"line":50,"column":0},"end":{"line":50,"column":20}},"50":{"start":{"line":51,"column":0},"end":{"line":51,"column":44}},"51":{"start":{"line":52,"column":0},"end":{"line":52,"column":9}},"52":{"start":{"line":53,"column":0},"end":{"line":53,"column":13}},"53":{"start":{"line":54,"column":0},"end":{"line":54,"column":5}},"56":{"start":{"line":57,"column":0},"end":{"line":57,"column":32}},"59":{"start":{"line":60,"column":0},"end":{"line":60,"column":73}},"60":{"start":{"line":61,"column":0},"end":{"line":61,"column":28}},"61":{"start":{"line":62,"column":0},"end":{"line":62,"column":16}},"62":{"start":{"line":63,"column":0},"end":{"line":63,"column":78}},"63":{"start":{"line":64,"column":0},"end":{"line":64,"column":9}},"64":{"start":{"line":65,"column":0},"end":{"line":65,"column":13}},"65":{"start":{"line":66,"column":0},"end":{"line":66,"column":5}},"68":{"start":{"line":69,"column":0},"end":{"line":69,"column":11}},"69":{"start":{"line":70,"column":0},"end":{"line":70,"column":19}},"70":{"start":{"line":71,"column":0},"end":{"line":71,"column":45}},"71":{"start":{"line":72,"column":0},"end":{"line":72,"column":28}},"72":{"start":{"line":73,"column":0},"end":{"line":73,"column":44}},"73":{"start":{"line":74,"column":0},"end":{"line":74,"column":9}},"74":{"start":{"line":75,"column":0},"end":{"line":75,"column":13}},"75":{"start":{"line":76,"column":0},"end":{"line":76,"column":5}},"76":{"start":{"line":77,"column":0},"end":{"line":77,"column":45}},"77":{"start":{"line":78,"column":0},"end":{"line":78,"column":28}},"78":{"start":{"line":79,"column":0},"end":{"line":79,"column":33}},"79":{"start":{"line":80,"column":0},"end":{"line":80,"column":9}},"80":{"start":{"line":81,"column":0},"end":{"line":81,"column":13}},"81":{"start":{"line":82,"column":0},"end":{"line":82,"column":5}},"83":{"start":{"line":84,"column":0},"end":{"line":84,"column":52}},"84":{"start":{"line":85,"column":0},"end":{"line":85,"column":3}},"85":{"start":{"line":86,"column":0},"end":{"line":86,"column":2}},"87":{"start":{"line":88,"column":0},"end":{"line":88,"column":25}}},"s":{"1":1,"6":1,"16":1,"17":0,"18":0,"19":0,"20":0,"21":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0,"32":0,"33":0,"34":0,"35":0,"36":0,"37":0,"38":0,"41":0,"42":0,"43":0,"44":0,"47":0,"48":0,"49":0,"50":0,"51":0,"52":0,"53":0,"56":0,"59":0,"60":0,"61":0,"62":0,"63":0,"64":0,"65":0,"68":0,"69":0,"70":0,"71":0,"72":0,"73":0,"74":0,"75":0,"76":0,"77":0,"78":0,"79":0,"80":0,"81":0,"83":0,"84":0,"85":0,"87":1},"branchMap":{},"b":{},"fnMap":{"0":{"name":"authCheck","decl":{"start":{"line":17,"column":18},"end":{"line":86,"column":2}},"loc":{"start":{"line":17,"column":18},"end":{"line":86,"column":2}},"line":17}},"f":{"0":0}} +,"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/src/middleware/errorHandler.ts": {"path":"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/src/middleware/errorHandler.ts","all":false,"statementMap":{"1":{"start":{"line":2,"column":0},"end":{"line":2,"column":46}},"9":{"start":{"line":10,"column":0},"end":{"line":10,"column":22}},"10":{"start":{"line":11,"column":0},"end":{"line":11,"column":24}},"11":{"start":{"line":12,"column":0},"end":{"line":12,"column":16}},"12":{"start":{"line":13,"column":0},"end":{"line":13,"column":16}},"13":{"start":{"line":14,"column":0},"end":{"line":14,"column":22}},"14":{"start":{"line":15,"column":0},"end":{"line":15,"column":12}},"15":{"start":{"line":16,"column":0},"end":{"line":16,"column":44}},"18":{"start":{"line":19,"column":0},"end":{"line":19,"column":68}},"19":{"start":{"line":20,"column":0},"end":{"line":20,"column":72}},"22":{"start":{"line":23,"column":0},"end":{"line":23,"column":40}},"23":{"start":{"line":24,"column":0},"end":{"line":24,"column":11}},"24":{"start":{"line":25,"column":0},"end":{"line":25,"column":59}},"25":{"start":{"line":26,"column":0},"end":{"line":26,"column":4}},"28":{"start":{"line":29,"column":0},"end":{"line":29,"column":47}},"29":{"start":{"line":30,"column":0},"end":{"line":30,"column":36}},"30":{"start":{"line":31,"column":0},"end":{"line":31,"column":3}},"32":{"start":{"line":33,"column":0},"end":{"line":33,"column":56}},"33":{"start":{"line":34,"column":0},"end":{"line":34,"column":45}},"34":{"start":{"line":35,"column":0},"end":{"line":35,"column":2}},"36":{"start":{"line":37,"column":0},"end":{"line":37,"column":28}}},"s":{"1":1,"9":1,"10":4,"11":4,"12":4,"13":4,"14":4,"15":4,"18":4,"19":4,"22":4,"23":4,"24":4,"25":4,"28":4,"29":0,"30":0,"32":4,"33":4,"34":4,"36":1},"branchMap":{"0":{"type":"branch","line":10,"loc":{"start":{"line":10,"column":21},"end":{"line":35,"column":2}},"locations":[{"start":{"line":10,"column":21},"end":{"line":35,"column":2}}]},"1":{"type":"branch","line":19,"loc":{"start":{"line":19,"column":51},"end":{"line":19,"column":68}},"locations":[{"start":{"line":19,"column":51},"end":{"line":19,"column":68}}]},"2":{"type":"branch","line":25,"loc":{"start":{"line":25,"column":17},"end":{"line":25,"column":59}},"locations":[{"start":{"line":25,"column":17},"end":{"line":25,"column":59}}]},"3":{"type":"branch","line":29,"loc":{"start":{"line":29,"column":46},"end":{"line":31,"column":3}},"locations":[{"start":{"line":29,"column":46},"end":{"line":31,"column":3}}]}},"b":{"0":[4],"1":[0],"2":[0],"3":[0]},"fnMap":{"0":{"name":"errorHandler","decl":{"start":{"line":10,"column":21},"end":{"line":35,"column":2}},"loc":{"start":{"line":10,"column":21},"end":{"line":35,"column":2}},"line":10}},"f":{"0":4}} +,"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/src/middleware/handleExpoResponse.ts": {"path":"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/src/middleware/handleExpoResponse.ts","all":false,"statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":49}},"1":{"start":{"line":2,"column":0},"end":{"line":2,"column":34}},"2":{"start":{"line":3,"column":0},"end":{"line":3,"column":46}},"34":{"start":{"line":35,"column":0},"end":{"line":35,"column":40}},"35":{"start":{"line":36,"column":0},"end":{"line":36,"column":23}},"36":{"start":{"line":37,"column":0},"end":{"line":37,"column":17}},"37":{"start":{"line":38,"column":0},"end":{"line":38,"column":31}},"38":{"start":{"line":39,"column":0},"end":{"line":39,"column":21}},"39":{"start":{"line":40,"column":0},"end":{"line":40,"column":7}},"40":{"start":{"line":41,"column":0},"end":{"line":41,"column":33}},"41":{"start":{"line":42,"column":0},"end":{"line":42,"column":50}},"42":{"start":{"line":43,"column":0},"end":{"line":43,"column":15}},"43":{"start":{"line":44,"column":0},"end":{"line":44,"column":35}},"44":{"start":{"line":45,"column":0},"end":{"line":45,"column":39}},"45":{"start":{"line":46,"column":0},"end":{"line":46,"column":22}},"46":{"start":{"line":47,"column":0},"end":{"line":47,"column":8}},"48":{"start":{"line":49,"column":0},"end":{"line":49,"column":50}},"49":{"start":{"line":50,"column":0},"end":{"line":50,"column":13}},"50":{"start":{"line":51,"column":0},"end":{"line":51,"column":5}},"52":{"start":{"line":53,"column":0},"end":{"line":53,"column":8}},"53":{"start":{"line":54,"column":0},"end":{"line":54,"column":34}},"54":{"start":{"line":55,"column":0},"end":{"line":55,"column":53}},"55":{"start":{"line":56,"column":0},"end":{"line":56,"column":7}},"57":{"start":{"line":58,"column":0},"end":{"line":58,"column":45}},"58":{"start":{"line":59,"column":0},"end":{"line":59,"column":32}},"59":{"start":{"line":60,"column":0},"end":{"line":60,"column":34}},"60":{"start":{"line":61,"column":0},"end":{"line":61,"column":8}},"63":{"start":{"line":64,"column":0},"end":{"line":64,"column":34}},"64":{"start":{"line":65,"column":0},"end":{"line":65,"column":69}},"65":{"start":{"line":66,"column":0},"end":{"line":66,"column":7}},"66":{"start":{"line":67,"column":0},"end":{"line":67,"column":13}},"67":{"start":{"line":68,"column":0},"end":{"line":68,"column":5}},"70":{"start":{"line":71,"column":0},"end":{"line":71,"column":36}},"71":{"start":{"line":72,"column":0},"end":{"line":72,"column":25}},"72":{"start":{"line":73,"column":0},"end":{"line":73,"column":79}},"73":{"start":{"line":74,"column":0},"end":{"line":74,"column":12}},"74":{"start":{"line":75,"column":0},"end":{"line":75,"column":8}},"75":{"start":{"line":76,"column":0},"end":{"line":76,"column":5}},"76":{"start":{"line":77,"column":0},"end":{"line":77,"column":19}},"77":{"start":{"line":78,"column":0},"end":{"line":78,"column":36}},"78":{"start":{"line":79,"column":0},"end":{"line":79,"column":18}},"79":{"start":{"line":80,"column":0},"end":{"line":80,"column":5}},"80":{"start":{"line":81,"column":0},"end":{"line":81,"column":76}},"81":{"start":{"line":82,"column":0},"end":{"line":82,"column":3}},"82":{"start":{"line":83,"column":0},"end":{"line":83,"column":2}},"84":{"start":{"line":85,"column":0},"end":{"line":85,"column":40}}},"s":{"0":1,"1":1,"2":1,"34":1,"35":0,"36":0,"37":0,"38":0,"39":0,"40":0,"41":0,"42":0,"43":0,"44":0,"45":0,"46":0,"48":0,"49":0,"50":0,"52":0,"53":0,"54":0,"55":0,"57":0,"58":0,"59":0,"60":0,"63":0,"64":0,"65":0,"66":0,"67":0,"70":0,"71":0,"72":0,"73":0,"74":0,"75":0,"76":0,"77":0,"78":0,"79":0,"80":0,"81":0,"82":0,"84":1},"branchMap":{},"b":{},"fnMap":{"0":{"name":"expoNotificationResponse","decl":{"start":{"line":35,"column":33},"end":{"line":83,"column":2}},"loc":{"start":{"line":35,"column":33},"end":{"line":83,"column":2}},"line":35}},"f":{"0":0}} +,"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/src/middleware/multerStorage.ts": {"path":"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/src/middleware/multerStorage.ts","all":false,"statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":68}},"1":{"start":{"line":2,"column":0},"end":{"line":2,"column":24}},"3":{"start":{"line":4,"column":0},"end":{"line":4,"column":46}},"10":{"start":{"line":11,"column":0},"end":{"line":11,"column":51}},"11":{"start":{"line":12,"column":0},"end":{"line":12,"column":37}},"12":{"start":{"line":13,"column":0},"end":{"line":13,"column":36}},"13":{"start":{"line":14,"column":0},"end":{"line":14,"column":4}},"14":{"start":{"line":15,"column":0},"end":{"line":15,"column":33}},"15":{"start":{"line":16,"column":0},"end":{"line":16,"column":76}},"16":{"start":{"line":17,"column":0},"end":{"line":17,"column":7}},"17":{"start":{"line":18,"column":0},"end":{"line":18,"column":11}},"18":{"start":{"line":19,"column":0},"end":{"line":19,"column":76}},"19":{"start":{"line":20,"column":0},"end":{"line":20,"column":6}},"20":{"start":{"line":21,"column":0},"end":{"line":21,"column":4}},"21":{"start":{"line":22,"column":0},"end":{"line":22,"column":3}},"24":{"start":{"line":25,"column":0},"end":{"line":25,"column":31}},"25":{"start":{"line":26,"column":0},"end":{"line":26,"column":10}},"26":{"start":{"line":27,"column":0},"end":{"line":27,"column":11}},"27":{"start":{"line":28,"column":0},"end":{"line":28,"column":51}},"28":{"start":{"line":29,"column":0},"end":{"line":29,"column":4}},"29":{"start":{"line":30,"column":0},"end":{"line":30,"column":35}},"31":{"start":{"line":32,"column":0},"end":{"line":32,"column":45}},"32":{"start":{"line":33,"column":0},"end":{"line":33,"column":52}},"33":{"start":{"line":34,"column":0},"end":{"line":34,"column":13}},"34":{"start":{"line":35,"column":0},"end":{"line":35,"column":5}},"35":{"start":{"line":36,"column":0},"end":{"line":36,"column":19}},"36":{"start":{"line":37,"column":0},"end":{"line":37,"column":4}},"37":{"start":{"line":38,"column":0},"end":{"line":38,"column":3}},"40":{"start":{"line":41,"column":0},"end":{"line":41,"column":49}},"41":{"start":{"line":42,"column":0},"end":{"line":42,"column":46}},"43":{"start":{"line":44,"column":0},"end":{"line":44,"column":10}},"44":{"start":{"line":45,"column":0},"end":{"line":45,"column":23}},"45":{"start":{"line":46,"column":0},"end":{"line":46,"column":24}},"46":{"start":{"line":47,"column":0},"end":{"line":47,"column":23}},"47":{"start":{"line":48,"column":0},"end":{"line":48,"column":14}},"49":{"start":{"line":50,"column":0},"end":{"line":50,"column":48}},"50":{"start":{"line":51,"column":0},"end":{"line":51,"column":49}},"51":{"start":{"line":52,"column":0},"end":{"line":52,"column":55}},"56":{"start":{"line":57,"column":0},"end":{"line":57,"column":62}},"57":{"start":{"line":58,"column":0},"end":{"line":58,"column":39}},"58":{"start":{"line":59,"column":0},"end":{"line":59,"column":45}},"59":{"start":{"line":60,"column":0},"end":{"line":60,"column":78}},"60":{"start":{"line":61,"column":0},"end":{"line":61,"column":17}},"61":{"start":{"line":62,"column":0},"end":{"line":62,"column":9}},"62":{"start":{"line":63,"column":0},"end":{"line":63,"column":45}},"63":{"start":{"line":64,"column":0},"end":{"line":64,"column":15}},"64":{"start":{"line":65,"column":0},"end":{"line":65,"column":7}},"65":{"start":{"line":66,"column":0},"end":{"line":66,"column":33}},"66":{"start":{"line":67,"column":0},"end":{"line":67,"column":45}},"67":{"start":{"line":68,"column":0},"end":{"line":68,"column":15}},"68":{"start":{"line":69,"column":0},"end":{"line":69,"column":7}},"69":{"start":{"line":70,"column":0},"end":{"line":70,"column":13}},"70":{"start":{"line":71,"column":0},"end":{"line":71,"column":7}},"71":{"start":{"line":72,"column":0},"end":{"line":72,"column":4}},"72":{"start":{"line":73,"column":0},"end":{"line":73,"column":2}},"74":{"start":{"line":75,"column":0},"end":{"line":75,"column":32}}},"s":{"0":1,"1":1,"3":1,"10":1,"11":1,"12":0,"13":0,"14":1,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":1,"24":1,"25":1,"26":1,"27":1,"28":1,"29":1,"31":0,"32":0,"33":0,"34":0,"35":0,"36":0,"37":1,"40":1,"41":8,"43":8,"44":0,"45":0,"46":0,"47":0,"49":0,"50":0,"51":0,"56":0,"57":0,"58":0,"59":0,"60":0,"61":0,"62":0,"63":0,"64":0,"65":0,"66":0,"67":0,"68":0,"69":0,"70":0,"71":0,"72":8,"74":1},"branchMap":{"0":{"type":"branch","line":41,"loc":{"start":{"line":41,"column":25},"end":{"line":73,"column":2}},"locations":[{"start":{"line":41,"column":25},"end":{"line":73,"column":2}}]}},"b":{"0":[8]},"fnMap":{"0":{"name":"destination","decl":{"start":{"line":12,"column":15},"end":{"line":14,"column":4}},"loc":{"start":{"line":12,"column":15},"end":{"line":14,"column":4}},"line":12},"1":{"name":"filename","decl":{"start":{"line":15,"column":12},"end":{"line":21,"column":4}},"loc":{"start":{"line":15,"column":12},"end":{"line":21,"column":4}},"line":15},"2":{"name":"fileFilter","decl":{"start":{"line":30,"column":14},"end":{"line":37,"column":4}},"loc":{"start":{"line":30,"column":14},"end":{"line":37,"column":4}},"line":30},"3":{"name":"uploadMiddleware","decl":{"start":{"line":41,"column":25},"end":{"line":73,"column":2}},"loc":{"start":{"line":41,"column":25},"end":{"line":73,"column":2}},"line":41}},"f":{"0":0,"1":0,"2":0,"3":8}} +,"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/src/middleware/rateLimiter.ts": {"path":"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/src/middleware/rateLimiter.ts","all":false,"statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":56}},"1":{"start":{"line":2,"column":0},"end":{"line":2,"column":46}},"4":{"start":{"line":5,"column":0},"end":{"line":5,"column":43}},"5":{"start":{"line":6,"column":0},"end":{"line":6,"column":41}},"6":{"start":{"line":7,"column":0},"end":{"line":7,"column":53}},"7":{"start":{"line":8,"column":0},"end":{"line":8,"column":79}},"8":{"start":{"line":9,"column":0},"end":{"line":9,"column":62}},"9":{"start":{"line":10,"column":0},"end":{"line":10,"column":68}},"10":{"start":{"line":11,"column":0},"end":{"line":11,"column":39}},"11":{"start":{"line":12,"column":0},"end":{"line":12,"column":18}},"12":{"start":{"line":13,"column":0},"end":{"line":13,"column":23}},"13":{"start":{"line":14,"column":0},"end":{"line":14,"column":63}},"14":{"start":{"line":15,"column":0},"end":{"line":15,"column":10}},"15":{"start":{"line":16,"column":0},"end":{"line":16,"column":6}},"16":{"start":{"line":17,"column":0},"end":{"line":17,"column":4}},"17":{"start":{"line":18,"column":0},"end":{"line":18,"column":18}},"19":{"start":{"line":20,"column":0},"end":{"line":20,"column":34}},"20":{"start":{"line":21,"column":0},"end":{"line":21,"column":4}},"21":{"start":{"line":22,"column":0},"end":{"line":22,"column":2}},"23":{"start":{"line":24,"column":0},"end":{"line":24,"column":43}},"25":{"start":{"line":26,"column":0},"end":{"line":26,"column":23}}},"s":{"0":1,"1":1,"4":1,"5":1,"6":1,"7":1,"8":1,"9":1,"10":1,"11":1,"12":0,"13":0,"14":0,"15":0,"16":0,"17":1,"19":4,"20":4,"21":1,"23":1,"25":1},"branchMap":{"0":{"type":"branch","line":18,"loc":{"start":{"line":18,"column":8},"end":{"line":21,"column":4}},"locations":[{"start":{"line":18,"column":8},"end":{"line":21,"column":4}}]}},"b":{"0":[4]},"fnMap":{"0":{"name":"handler","decl":{"start":{"line":12,"column":11},"end":{"line":17,"column":4}},"loc":{"start":{"line":12,"column":11},"end":{"line":17,"column":4}},"line":12},"1":{"name":"skip","decl":{"start":{"line":18,"column":8},"end":{"line":21,"column":4}},"loc":{"start":{"line":18,"column":8},"end":{"line":21,"column":4}},"line":18}},"f":{"0":0,"1":4}} +,"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/src/middleware/send-notification.ts": {"path":"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/src/middleware/send-notification.ts","all":false,"statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":45}},"1":{"start":{"line":2,"column":0},"end":{"line":2,"column":46}},"32":{"start":{"line":33,"column":0},"end":{"line":33,"column":42}},"33":{"start":{"line":34,"column":0},"end":{"line":34,"column":27}},"34":{"start":{"line":35,"column":0},"end":{"line":35,"column":31}},"35":{"start":{"line":36,"column":0},"end":{"line":36,"column":41}},"36":{"start":{"line":37,"column":0},"end":{"line":37,"column":19}},"37":{"start":{"line":38,"column":0},"end":{"line":38,"column":21}},"38":{"start":{"line":39,"column":0},"end":{"line":39,"column":3}},"40":{"start":{"line":41,"column":0},"end":{"line":41,"column":32}},"41":{"start":{"line":42,"column":0},"end":{"line":42,"column":18}},"42":{"start":{"line":43,"column":0},"end":{"line":43,"column":21}},"43":{"start":{"line":44,"column":0},"end":{"line":44,"column":31}},"44":{"start":{"line":45,"column":0},"end":{"line":45,"column":29}},"45":{"start":{"line":46,"column":0},"end":{"line":46,"column":21}},"46":{"start":{"line":47,"column":0},"end":{"line":47,"column":4}},"48":{"start":{"line":49,"column":0},"end":{"line":49,"column":7}},"49":{"start":{"line":50,"column":0},"end":{"line":50,"column":43}},"50":{"start":{"line":51,"column":0},"end":{"line":51,"column":45}},"51":{"start":{"line":52,"column":0},"end":{"line":52,"column":7}},"52":{"start":{"line":53,"column":0},"end":{"line":53,"column":23}},"53":{"start":{"line":54,"column":0},"end":{"line":54,"column":18}},"54":{"start":{"line":55,"column":0},"end":{"line":55,"column":37}},"55":{"start":{"line":56,"column":0},"end":{"line":56,"column":45}},"56":{"start":{"line":57,"column":0},"end":{"line":57,"column":10}},"57":{"start":{"line":58,"column":0},"end":{"line":58,"column":38}},"58":{"start":{"line":59,"column":0},"end":{"line":59,"column":8}},"59":{"start":{"line":60,"column":0},"end":{"line":60,"column":6}},"61":{"start":{"line":62,"column":0},"end":{"line":62,"column":23}},"62":{"start":{"line":63,"column":0},"end":{"line":63,"column":25}},"63":{"start":{"line":64,"column":0},"end":{"line":64,"column":62}},"64":{"start":{"line":65,"column":0},"end":{"line":65,"column":24}},"65":{"start":{"line":66,"column":0},"end":{"line":66,"column":8}},"66":{"start":{"line":67,"column":0},"end":{"line":67,"column":5}},"68":{"start":{"line":69,"column":0},"end":{"line":69,"column":59}},"71":{"start":{"line":72,"column":0},"end":{"line":72,"column":71}},"72":{"start":{"line":73,"column":0},"end":{"line":73,"column":16}},"73":{"start":{"line":74,"column":0},"end":{"line":74,"column":75}},"74":{"start":{"line":75,"column":0},"end":{"line":75,"column":5}},"76":{"start":{"line":77,"column":0},"end":{"line":77,"column":18}},"77":{"start":{"line":78,"column":0},"end":{"line":78,"column":19}},"78":{"start":{"line":79,"column":0},"end":{"line":79,"column":36}},"79":{"start":{"line":80,"column":0},"end":{"line":80,"column":18}},"80":{"start":{"line":81,"column":0},"end":{"line":81,"column":5}},"81":{"start":{"line":82,"column":0},"end":{"line":82,"column":33}},"82":{"start":{"line":83,"column":0},"end":{"line":83,"column":25}},"83":{"start":{"line":84,"column":0},"end":{"line":84,"column":61}},"84":{"start":{"line":85,"column":0},"end":{"line":85,"column":12}},"85":{"start":{"line":86,"column":0},"end":{"line":86,"column":8}},"86":{"start":{"line":87,"column":0},"end":{"line":87,"column":5}},"87":{"start":{"line":88,"column":0},"end":{"line":88,"column":64}},"88":{"start":{"line":89,"column":0},"end":{"line":89,"column":3}},"89":{"start":{"line":90,"column":0},"end":{"line":90,"column":2}},"91":{"start":{"line":92,"column":0},"end":{"line":92,"column":42}}},"s":{"0":1,"1":1,"32":1,"33":0,"34":0,"35":0,"36":0,"37":0,"38":0,"40":0,"41":0,"42":0,"43":0,"44":0,"45":0,"46":0,"48":0,"49":0,"50":0,"51":0,"52":0,"53":0,"54":0,"55":0,"56":0,"57":0,"58":0,"59":0,"61":0,"62":0,"63":0,"64":0,"65":0,"66":0,"68":0,"71":0,"72":0,"73":0,"74":0,"76":0,"77":0,"78":0,"79":0,"80":0,"81":0,"82":0,"83":0,"84":0,"85":0,"86":0,"87":0,"88":0,"89":0,"91":1},"branchMap":{},"b":{},"fnMap":{"0":{"name":"sendPushNotificationToUser","decl":{"start":{"line":33,"column":35},"end":{"line":90,"column":2}},"loc":{"start":{"line":33,"column":35},"end":{"line":90,"column":2}},"line":33}},"f":{"0":0}} +,"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/src/models/buyerreview.ts": {"path":"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/src/models/buyerreview.ts","all":false,"statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":8}},"11":{"start":{"line":12,"column":0},"end":{"line":12,"column":32}},"12":{"start":{"line":13,"column":0},"end":{"line":13,"column":26}},"13":{"start":{"line":14,"column":0},"end":{"line":14,"column":28}},"15":{"start":{"line":16,"column":0},"end":{"line":16,"column":8}},"16":{"start":{"line":17,"column":0},"end":{"line":17,"column":19}},"17":{"start":{"line":18,"column":0},"end":{"line":18,"column":28}},"18":{"start":{"line":19,"column":0},"end":{"line":19,"column":27}},"19":{"start":{"line":20,"column":0},"end":{"line":20,"column":2}},"20":{"start":{"line":21,"column":0},"end":{"line":21,"column":56}},"21":{"start":{"line":22,"column":0},"end":{"line":22,"column":11}},"22":{"start":{"line":23,"column":0},"end":{"line":23,"column":21}},"23":{"start":{"line":24,"column":0},"end":{"line":24,"column":24}},"24":{"start":{"line":25,"column":0},"end":{"line":25,"column":34}},"25":{"start":{"line":26,"column":0},"end":{"line":26,"column":21}},"26":{"start":{"line":27,"column":0},"end":{"line":27,"column":4}},"27":{"start":{"line":28,"column":0},"end":{"line":28,"column":21}},"28":{"start":{"line":29,"column":0},"end":{"line":29,"column":11}},"29":{"start":{"line":30,"column":0},"end":{"line":30,"column":26}},"30":{"start":{"line":31,"column":0},"end":{"line":31,"column":4}},"31":{"start":{"line":32,"column":0},"end":{"line":32,"column":26}},"32":{"start":{"line":33,"column":0},"end":{"line":33,"column":11}},"33":{"start":{"line":34,"column":0},"end":{"line":34,"column":27}},"34":{"start":{"line":35,"column":0},"end":{"line":35,"column":4}},"35":{"start":{"line":36,"column":0},"end":{"line":36,"column":25}},"37":{"start":{"line":38,"column":0},"end":{"line":38,"column":25}},"38":{"start":{"line":39,"column":0},"end":{"line":39,"column":11}},"39":{"start":{"line":40,"column":0},"end":{"line":40,"column":24}},"40":{"start":{"line":41,"column":0},"end":{"line":41,"column":4}},"41":{"start":{"line":42,"column":0},"end":{"line":42,"column":18}},"43":{"start":{"line":44,"column":0},"end":{"line":44,"column":28}},"44":{"start":{"line":45,"column":0},"end":{"line":45,"column":11}},"45":{"start":{"line":46,"column":0},"end":{"line":46,"column":24}},"46":{"start":{"line":47,"column":0},"end":{"line":47,"column":4}},"47":{"start":{"line":48,"column":0},"end":{"line":48,"column":18}},"49":{"start":{"line":50,"column":0},"end":{"line":50,"column":26}},"50":{"start":{"line":51,"column":0},"end":{"line":51,"column":11}},"51":{"start":{"line":52,"column":0},"end":{"line":52,"column":24}},"52":{"start":{"line":53,"column":0},"end":{"line":53,"column":4}},"53":{"start":{"line":54,"column":0},"end":{"line":54,"column":19}},"55":{"start":{"line":56,"column":0},"end":{"line":56,"column":24}},"56":{"start":{"line":57,"column":0},"end":{"line":57,"column":14}},"58":{"start":{"line":59,"column":0},"end":{"line":59,"column":27}},"59":{"start":{"line":60,"column":0},"end":{"line":60,"column":20}},"61":{"start":{"line":62,"column":0},"end":{"line":62,"column":25}},"62":{"start":{"line":63,"column":0},"end":{"line":63,"column":16}},"64":{"start":{"line":65,"column":0},"end":{"line":65,"column":12}},"65":{"start":{"line":66,"column":0},"end":{"line":66,"column":26}},"66":{"start":{"line":67,"column":0},"end":{"line":67,"column":12}},"67":{"start":{"line":68,"column":0},"end":{"line":68,"column":26}},"68":{"start":{"line":69,"column":0},"end":{"line":69,"column":1}},"70":{"start":{"line":71,"column":0},"end":{"line":71,"column":27}}},"s":{"0":1,"11":1,"12":1,"13":1,"15":1,"16":1,"17":1,"18":1,"19":1,"20":1,"21":1,"22":1,"23":1,"24":1,"25":1,"26":1,"27":1,"28":1,"29":1,"30":1,"31":1,"32":1,"33":1,"34":1,"35":1,"37":1,"38":1,"39":1,"40":1,"41":1,"43":1,"44":1,"45":1,"46":1,"47":1,"49":1,"50":1,"51":1,"52":1,"53":1,"55":1,"56":1,"58":1,"59":1,"61":1,"62":1,"64":1,"65":1,"66":1,"67":1,"68":1,"70":1},"branchMap":{},"b":{},"fnMap":{},"f":{}} +,"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/src/models/index.ts": {"path":"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/src/models/index.ts","all":true,"statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":28}},"1":{"start":{"line":2,"column":0},"end":{"line":2,"column":16}},"2":{"start":{"line":3,"column":0},"end":{"line":3,"column":28}},"3":{"start":{"line":4,"column":0},"end":{"line":4,"column":20}},"5":{"start":{"line":6,"column":0},"end":{"line":6,"column":49}},"6":{"start":{"line":7,"column":0},"end":{"line":7,"column":51}},"7":{"start":{"line":8,"column":0},"end":{"line":8,"column":38}},"24":{"start":{"line":25,"column":0},"end":{"line":25,"column":58}},"25":{"start":{"line":26,"column":0},"end":{"line":26,"column":61}},"27":{"start":{"line":28,"column":0},"end":{"line":28,"column":17}},"28":{"start":{"line":29,"column":0},"end":{"line":29,"column":76}},"29":{"start":{"line":30,"column":0},"end":{"line":30,"column":1}},"32":{"start":{"line":33,"column":0},"end":{"line":33,"column":50}},"35":{"start":{"line":36,"column":0},"end":{"line":36,"column":36}},"36":{"start":{"line":37,"column":0},"end":{"line":37,"column":30}},"37":{"start":{"line":38,"column":0},"end":{"line":38,"column":7}},"38":{"start":{"line":39,"column":0},"end":{"line":39,"column":49}},"39":{"start":{"line":40,"column":0},"end":{"line":40,"column":21}},"40":{"start":{"line":41,"column":0},"end":{"line":41,"column":14}},"41":{"start":{"line":42,"column":0},"end":{"line":42,"column":17}},"42":{"start":{"line":43,"column":0},"end":{"line":43,"column":59}},"43":{"start":{"line":44,"column":0},"end":{"line":44,"column":36}},"44":{"start":{"line":45,"column":0},"end":{"line":45,"column":7}},"45":{"start":{"line":46,"column":0},"end":{"line":46,"column":44}},"46":{"start":{"line":47,"column":0},"end":{"line":47,"column":19}},"47":{"start":{"line":48,"column":0},"end":{"line":48,"column":50}},"48":{"start":{"line":49,"column":0},"end":{"line":49,"column":46}},"49":{"start":{"line":50,"column":0},"end":{"line":50,"column":3}},"50":{"start":{"line":51,"column":0},"end":{"line":51,"column":2}},"52":{"start":{"line":53,"column":0},"end":{"line":53,"column":28}},"53":{"start":{"line":54,"column":0},"end":{"line":54,"column":26}},"54":{"start":{"line":55,"column":0},"end":{"line":55,"column":62}},"55":{"start":{"line":56,"column":0},"end":{"line":56,"column":1}},"57":{"start":{"line":58,"column":0},"end":{"line":58,"column":56}},"58":{"start":{"line":59,"column":0},"end":{"line":59,"column":22}},"59":{"start":{"line":60,"column":0},"end":{"line":60,"column":19}},"60":{"start":{"line":61,"column":0},"end":{"line":61,"column":10}},"61":{"start":{"line":62,"column":0},"end":{"line":62,"column":20}},"62":{"start":{"line":63,"column":0},"end":{"line":63,"column":97}},"63":{"start":{"line":64,"column":0},"end":{"line":64,"column":6}},"64":{"start":{"line":65,"column":0},"end":{"line":65,"column":4}},"65":{"start":{"line":66,"column":0},"end":{"line":66,"column":68}},"66":{"start":{"line":67,"column":0},"end":{"line":67,"column":17}},"67":{"start":{"line":68,"column":0},"end":{"line":68,"column":3}},"82":{"start":{"line":83,"column":0},"end":{"line":83,"column":14}},"83":{"start":{"line":84,"column":0},"end":{"line":84,"column":7}},"84":{"start":{"line":85,"column":0},"end":{"line":85,"column":40}},"85":{"start":{"line":86,"column":0},"end":{"line":86,"column":46}},"86":{"start":{"line":87,"column":0},"end":{"line":87,"column":44}},"87":{"start":{"line":88,"column":0},"end":{"line":88,"column":54}},"88":{"start":{"line":89,"column":0},"end":{"line":89,"column":15}},"89":{"start":{"line":90,"column":0},"end":{"line":90,"column":37}},"90":{"start":{"line":91,"column":0},"end":{"line":91,"column":43}},"91":{"start":{"line":92,"column":0},"end":{"line":92,"column":7}},"93":{"start":{"line":94,"column":0},"end":{"line":94,"column":52}},"96":{"start":{"line":97,"column":0},"end":{"line":97,"column":69}},"98":{"start":{"line":99,"column":0},"end":{"line":99,"column":11}},"100":{"start":{"line":101,"column":0},"end":{"line":101,"column":77}},"103":{"start":{"line":104,"column":0},"end":{"line":104,"column":37}},"106":{"start":{"line":107,"column":0},"end":{"line":107,"column":51}},"107":{"start":{"line":108,"column":0},"end":{"line":108,"column":67}},"111":{"start":{"line":112,"column":0},"end":{"line":112,"column":44}},"114":{"start":{"line":115,"column":0},"end":{"line":115,"column":75}},"115":{"start":{"line":116,"column":0},"end":{"line":116,"column":24}},"116":{"start":{"line":117,"column":0},"end":{"line":117,"column":13}},"118":{"start":{"line":119,"column":0},"end":{"line":119,"column":29}},"119":{"start":{"line":120,"column":0},"end":{"line":120,"column":74}},"120":{"start":{"line":121,"column":0},"end":{"line":121,"column":36}},"121":{"start":{"line":122,"column":0},"end":{"line":122,"column":18}},"122":{"start":{"line":123,"column":0},"end":{"line":123,"column":11}},"125":{"start":{"line":126,"column":0},"end":{"line":126,"column":70}},"126":{"start":{"line":127,"column":0},"end":{"line":127,"column":22}},"127":{"start":{"line":128,"column":0},"end":{"line":128,"column":15}},"128":{"start":{"line":129,"column":0},"end":{"line":129,"column":12}},"131":{"start":{"line":132,"column":0},"end":{"line":132,"column":54}},"134":{"start":{"line":135,"column":0},"end":{"line":135,"column":56}},"135":{"start":{"line":136,"column":0},"end":{"line":136,"column":61}},"138":{"start":{"line":139,"column":0},"end":{"line":139,"column":71}},"141":{"start":{"line":142,"column":0},"end":{"line":142,"column":63}},"144":{"start":{"line":145,"column":0},"end":{"line":145,"column":30}},"145":{"start":{"line":146,"column":0},"end":{"line":146,"column":26}},"146":{"start":{"line":147,"column":0},"end":{"line":147,"column":83}},"147":{"start":{"line":148,"column":0},"end":{"line":148,"column":16}},"148":{"start":{"line":149,"column":0},"end":{"line":149,"column":38}},"149":{"start":{"line":150,"column":0},"end":{"line":150,"column":20}},"150":{"start":{"line":151,"column":0},"end":{"line":151,"column":13}},"152":{"start":{"line":153,"column":0},"end":{"line":153,"column":65}},"153":{"start":{"line":154,"column":0},"end":{"line":154,"column":43}},"156":{"start":{"line":157,"column":0},"end":{"line":157,"column":59}},"157":{"start":{"line":158,"column":0},"end":{"line":158,"column":26}},"158":{"start":{"line":159,"column":0},"end":{"line":159,"column":88}},"159":{"start":{"line":160,"column":0},"end":{"line":160,"column":16}},"160":{"start":{"line":161,"column":0},"end":{"line":161,"column":38}},"161":{"start":{"line":162,"column":0},"end":{"line":162,"column":20}},"162":{"start":{"line":163,"column":0},"end":{"line":163,"column":13}},"163":{"start":{"line":164,"column":0},"end":{"line":164,"column":11}},"165":{"start":{"line":166,"column":0},"end":{"line":166,"column":38}},"168":{"start":{"line":169,"column":0},"end":{"line":169,"column":54}},"169":{"start":{"line":170,"column":0},"end":{"line":170,"column":16}},"170":{"start":{"line":171,"column":0},"end":{"line":171,"column":49}},"171":{"start":{"line":172,"column":0},"end":{"line":172,"column":68}},"172":{"start":{"line":173,"column":0},"end":{"line":173,"column":15}},"174":{"start":{"line":175,"column":0},"end":{"line":175,"column":26}},"175":{"start":{"line":176,"column":0},"end":{"line":176,"column":84}},"176":{"start":{"line":177,"column":0},"end":{"line":177,"column":16}},"177":{"start":{"line":178,"column":0},"end":{"line":178,"column":38}},"178":{"start":{"line":179,"column":0},"end":{"line":179,"column":20}},"179":{"start":{"line":180,"column":0},"end":{"line":180,"column":13}},"180":{"start":{"line":181,"column":0},"end":{"line":181,"column":11}},"182":{"start":{"line":183,"column":0},"end":{"line":183,"column":38}},"183":{"start":{"line":184,"column":0},"end":{"line":184,"column":9}},"186":{"start":{"line":187,"column":0},"end":{"line":187,"column":31}},"187":{"start":{"line":188,"column":0},"end":{"line":188,"column":37}},"188":{"start":{"line":189,"column":0},"end":{"line":189,"column":69}},"189":{"start":{"line":190,"column":0},"end":{"line":190,"column":41}},"190":{"start":{"line":191,"column":0},"end":{"line":191,"column":62}},"191":{"start":{"line":192,"column":0},"end":{"line":192,"column":16}},"192":{"start":{"line":193,"column":0},"end":{"line":193,"column":37}},"193":{"start":{"line":194,"column":0},"end":{"line":194,"column":74}},"194":{"start":{"line":195,"column":0},"end":{"line":195,"column":9}},"195":{"start":{"line":196,"column":0},"end":{"line":196,"column":21}},"197":{"start":{"line":198,"column":0},"end":{"line":198,"column":37}},"198":{"start":{"line":199,"column":0},"end":{"line":199,"column":18}},"199":{"start":{"line":200,"column":0},"end":{"line":200,"column":7}},"200":{"start":{"line":201,"column":0},"end":{"line":201,"column":12}},"201":{"start":{"line":202,"column":0},"end":{"line":202,"column":47}},"202":{"start":{"line":203,"column":0},"end":{"line":203,"column":5}},"203":{"start":{"line":204,"column":0},"end":{"line":204,"column":19}},"204":{"start":{"line":205,"column":0},"end":{"line":205,"column":67}},"206":{"start":{"line":207,"column":0},"end":{"line":207,"column":39}},"207":{"start":{"line":208,"column":0},"end":{"line":208,"column":20}},"208":{"start":{"line":209,"column":0},"end":{"line":209,"column":91}},"209":{"start":{"line":210,"column":0},"end":{"line":210,"column":8}},"211":{"start":{"line":212,"column":0},"end":{"line":212,"column":12}},"212":{"start":{"line":213,"column":0},"end":{"line":213,"column":50}},"213":{"start":{"line":214,"column":0},"end":{"line":214,"column":5}},"214":{"start":{"line":215,"column":0},"end":{"line":215,"column":3}},"215":{"start":{"line":216,"column":0},"end":{"line":216,"column":5}},"218":{"start":{"line":219,"column":0},"end":{"line":219,"column":75}},"220":{"start":{"line":221,"column":0},"end":{"line":221,"column":37}},"221":{"start":{"line":222,"column":0},"end":{"line":222,"column":53}},"222":{"start":{"line":223,"column":0},"end":{"line":223,"column":40}},"223":{"start":{"line":224,"column":0},"end":{"line":224,"column":31}},"224":{"start":{"line":225,"column":0},"end":{"line":225,"column":30}},"225":{"start":{"line":226,"column":0},"end":{"line":226,"column":43}},"226":{"start":{"line":227,"column":0},"end":{"line":227,"column":36}},"227":{"start":{"line":228,"column":0},"end":{"line":228,"column":33}},"228":{"start":{"line":229,"column":0},"end":{"line":229,"column":47}},"229":{"start":{"line":230,"column":0},"end":{"line":230,"column":23}},"230":{"start":{"line":231,"column":0},"end":{"line":231,"column":19}},"231":{"start":{"line":232,"column":0},"end":{"line":232,"column":28}},"232":{"start":{"line":233,"column":0},"end":{"line":233,"column":21}},"233":{"start":{"line":234,"column":0},"end":{"line":234,"column":21}},"234":{"start":{"line":235,"column":0},"end":{"line":235,"column":4}},"237":{"start":{"line":238,"column":0},"end":{"line":238,"column":62}},"238":{"start":{"line":239,"column":0},"end":{"line":239,"column":48}},"241":{"start":{"line":242,"column":0},"end":{"line":242,"column":41}},"242":{"start":{"line":243,"column":0},"end":{"line":243,"column":65}},"243":{"start":{"line":244,"column":0},"end":{"line":244,"column":12}},"244":{"start":{"line":245,"column":0},"end":{"line":245,"column":1}},"246":{"start":{"line":247,"column":0},"end":{"line":247,"column":25}}},"s":{"0":0,"1":0,"2":0,"3":0,"5":0,"6":0,"7":0,"24":0,"25":0,"27":0,"28":0,"29":0,"32":0,"35":0,"36":0,"37":0,"38":0,"39":0,"40":0,"41":0,"42":0,"43":0,"44":0,"45":0,"46":0,"47":0,"48":0,"49":0,"50":0,"52":0,"53":0,"54":0,"55":0,"57":0,"58":0,"59":0,"60":0,"61":0,"62":0,"63":0,"64":0,"65":0,"66":0,"67":0,"82":0,"83":0,"84":0,"85":0,"86":0,"87":0,"88":0,"89":0,"90":0,"91":0,"93":0,"96":0,"98":0,"100":0,"103":0,"106":0,"107":0,"111":0,"114":0,"115":0,"116":0,"118":0,"119":0,"120":0,"121":0,"122":0,"125":0,"126":0,"127":0,"128":0,"131":0,"134":0,"135":0,"138":0,"141":0,"144":0,"145":0,"146":0,"147":0,"148":0,"149":0,"150":0,"152":0,"153":0,"156":0,"157":0,"158":0,"159":0,"160":0,"161":0,"162":0,"163":0,"165":0,"168":0,"169":0,"170":0,"171":0,"172":0,"174":0,"175":0,"176":0,"177":0,"178":0,"179":0,"180":0,"182":0,"183":0,"186":0,"187":0,"188":0,"189":0,"190":0,"191":0,"192":0,"193":0,"194":0,"195":0,"197":0,"198":0,"199":0,"200":0,"201":0,"202":0,"203":0,"204":0,"206":0,"207":0,"208":0,"209":0,"211":0,"212":0,"213":0,"214":0,"215":0,"218":0,"220":0,"221":0,"222":0,"223":0,"224":0,"225":0,"226":0,"227":0,"228":0,"229":0,"230":0,"231":0,"232":0,"233":0,"234":0,"237":0,"238":0,"241":0,"242":0,"243":0,"244":0,"246":0},"branchMap":{"0":{"type":"branch","line":1,"loc":{"start":{"line":1,"column":7875},"end":{"line":247,"column":25}},"locations":[{"start":{"line":1,"column":7875},"end":{"line":247,"column":25}}]}},"b":{"0":[0]},"fnMap":{"0":{"name":"(empty-report)","decl":{"start":{"line":1,"column":7875},"end":{"line":247,"column":25}},"loc":{"start":{"line":1,"column":7875},"end":{"line":247,"column":25}},"line":1}},"f":{"0":0}} +,"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/src/models/notifiation.ts": {"path":"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/src/models/notifiation.ts","all":false,"statementMap":{"1":{"start":{"line":2,"column":0},"end":{"line":2,"column":8}},"15":{"start":{"line":16,"column":0},"end":{"line":16,"column":26}},"17":{"start":{"line":18,"column":0},"end":{"line":18,"column":8}},"18":{"start":{"line":19,"column":0},"end":{"line":19,"column":19}},"19":{"start":{"line":20,"column":0},"end":{"line":20,"column":29}},"20":{"start":{"line":21,"column":0},"end":{"line":21,"column":28}},"21":{"start":{"line":22,"column":0},"end":{"line":22,"column":2}},"22":{"start":{"line":23,"column":0},"end":{"line":23,"column":33}},"25":{"start":{"line":26,"column":0},"end":{"line":26,"column":3}},"26":{"start":{"line":27,"column":0},"end":{"line":27,"column":11}},"27":{"start":{"line":28,"column":0},"end":{"line":28,"column":21}},"28":{"start":{"line":29,"column":0},"end":{"line":29,"column":24}},"29":{"start":{"line":30,"column":0},"end":{"line":30,"column":34}},"30":{"start":{"line":31,"column":0},"end":{"line":31,"column":21}},"31":{"start":{"line":32,"column":0},"end":{"line":32,"column":4}},"32":{"start":{"line":33,"column":0},"end":{"line":33,"column":29}},"33":{"start":{"line":34,"column":0},"end":{"line":34,"column":11}},"34":{"start":{"line":35,"column":0},"end":{"line":35,"column":26}},"35":{"start":{"line":36,"column":0},"end":{"line":36,"column":4}},"36":{"start":{"line":37,"column":0},"end":{"line":37,"column":25}},"37":{"start":{"line":38,"column":0},"end":{"line":38,"column":11}},"38":{"start":{"line":39,"column":0},"end":{"line":39,"column":26}},"39":{"start":{"line":40,"column":0},"end":{"line":40,"column":4}},"40":{"start":{"line":41,"column":0},"end":{"line":41,"column":27}},"41":{"start":{"line":42,"column":0},"end":{"line":42,"column":11}},"42":{"start":{"line":43,"column":0},"end":{"line":43,"column":27}},"43":{"start":{"line":44,"column":0},"end":{"line":44,"column":4}},"44":{"start":{"line":45,"column":0},"end":{"line":45,"column":26}},"45":{"start":{"line":46,"column":0},"end":{"line":46,"column":25}},"46":{"start":{"line":47,"column":0},"end":{"line":47,"column":11}},"47":{"start":{"line":48,"column":0},"end":{"line":48,"column":24}},"48":{"start":{"line":49,"column":0},"end":{"line":49,"column":4}},"49":{"start":{"line":50,"column":0},"end":{"line":50,"column":26}},"51":{"start":{"line":52,"column":0},"end":{"line":52,"column":24}},"52":{"start":{"line":53,"column":0},"end":{"line":53,"column":14}},"54":{"start":{"line":55,"column":0},"end":{"line":55,"column":12}},"55":{"start":{"line":56,"column":0},"end":{"line":56,"column":26}},"56":{"start":{"line":57,"column":0},"end":{"line":57,"column":12}},"57":{"start":{"line":58,"column":0},"end":{"line":58,"column":26}},"58":{"start":{"line":59,"column":0},"end":{"line":59,"column":1}},"60":{"start":{"line":61,"column":0},"end":{"line":61,"column":28}}},"s":{"1":1,"15":1,"17":1,"18":1,"19":1,"20":1,"21":1,"22":1,"25":1,"26":1,"27":1,"28":1,"29":1,"30":1,"31":1,"32":1,"33":1,"34":1,"35":1,"36":1,"37":1,"38":1,"39":1,"40":1,"41":1,"42":1,"43":1,"44":1,"45":1,"46":1,"47":1,"48":1,"49":1,"51":1,"52":1,"54":1,"55":1,"56":1,"57":1,"58":1,"60":1},"branchMap":{},"b":{},"fnMap":{},"f":{}} +,"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/src/models/order.ts": {"path":"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/src/models/order.ts","all":false,"statementMap":{"1":{"start":{"line":2,"column":0},"end":{"line":2,"column":8}},"13":{"start":{"line":14,"column":0},"end":{"line":14,"column":26}},"14":{"start":{"line":15,"column":0},"end":{"line":15,"column":40}},"15":{"start":{"line":16,"column":0},"end":{"line":16,"column":32}},"16":{"start":{"line":17,"column":0},"end":{"line":17,"column":40}},"18":{"start":{"line":19,"column":0},"end":{"line":19,"column":8}},"19":{"start":{"line":20,"column":0},"end":{"line":20,"column":19}},"20":{"start":{"line":21,"column":0},"end":{"line":21,"column":22}},"21":{"start":{"line":22,"column":0},"end":{"line":22,"column":21}},"22":{"start":{"line":23,"column":0},"end":{"line":23,"column":2}},"23":{"start":{"line":24,"column":0},"end":{"line":24,"column":44}},"24":{"start":{"line":25,"column":0},"end":{"line":25,"column":11}},"25":{"start":{"line":26,"column":0},"end":{"line":26,"column":21}},"26":{"start":{"line":27,"column":0},"end":{"line":27,"column":24}},"27":{"start":{"line":28,"column":0},"end":{"line":28,"column":34}},"28":{"start":{"line":29,"column":0},"end":{"line":29,"column":21}},"29":{"start":{"line":30,"column":0},"end":{"line":30,"column":4}},"30":{"start":{"line":31,"column":0},"end":{"line":31,"column":29}},"31":{"start":{"line":32,"column":0},"end":{"line":32,"column":11}},"32":{"start":{"line":33,"column":0},"end":{"line":33,"column":27}},"33":{"start":{"line":34,"column":0},"end":{"line":34,"column":4}},"34":{"start":{"line":35,"column":0},"end":{"line":35,"column":26}},"35":{"start":{"line":36,"column":0},"end":{"line":36,"column":11}},"36":{"start":{"line":37,"column":0},"end":{"line":37,"column":26}},"37":{"start":{"line":38,"column":0},"end":{"line":38,"column":4}},"38":{"start":{"line":39,"column":0},"end":{"line":39,"column":31}},"39":{"start":{"line":40,"column":0},"end":{"line":40,"column":11}},"40":{"start":{"line":41,"column":0},"end":{"line":41,"column":26}},"41":{"start":{"line":42,"column":0},"end":{"line":42,"column":4}},"42":{"start":{"line":43,"column":0},"end":{"line":43,"column":26}},"43":{"start":{"line":44,"column":0},"end":{"line":44,"column":11}},"44":{"start":{"line":45,"column":0},"end":{"line":45,"column":24}},"45":{"start":{"line":46,"column":0},"end":{"line":46,"column":4}},"46":{"start":{"line":47,"column":0},"end":{"line":47,"column":33}},"47":{"start":{"line":48,"column":0},"end":{"line":48,"column":11}},"48":{"start":{"line":49,"column":0},"end":{"line":49,"column":76}},"49":{"start":{"line":50,"column":0},"end":{"line":50,"column":4}},"50":{"start":{"line":51,"column":0},"end":{"line":51,"column":25}},"52":{"start":{"line":53,"column":0},"end":{"line":53,"column":28}},"53":{"start":{"line":54,"column":0},"end":{"line":54,"column":11}},"54":{"start":{"line":55,"column":0},"end":{"line":55,"column":24}},"55":{"start":{"line":56,"column":0},"end":{"line":56,"column":4}},"56":{"start":{"line":57,"column":0},"end":{"line":57,"column":18}},"58":{"start":{"line":59,"column":0},"end":{"line":59,"column":25}},"59":{"start":{"line":60,"column":0},"end":{"line":60,"column":34}},"60":{"start":{"line":61,"column":0},"end":{"line":61,"column":27}},"62":{"start":{"line":63,"column":0},"end":{"line":63,"column":25}},"63":{"start":{"line":64,"column":0},"end":{"line":64,"column":34}},"64":{"start":{"line":65,"column":0},"end":{"line":65,"column":28}},"65":{"start":{"line":66,"column":0},"end":{"line":66,"column":11}},"66":{"start":{"line":67,"column":0},"end":{"line":67,"column":24}},"67":{"start":{"line":68,"column":0},"end":{"line":68,"column":4}},"68":{"start":{"line":69,"column":0},"end":{"line":69,"column":42}},"69":{"start":{"line":70,"column":0},"end":{"line":70,"column":11}},"70":{"start":{"line":71,"column":0},"end":{"line":71,"column":27}},"71":{"start":{"line":72,"column":0},"end":{"line":72,"column":4}},"72":{"start":{"line":73,"column":0},"end":{"line":73,"column":30}},"73":{"start":{"line":74,"column":0},"end":{"line":74,"column":11}},"74":{"start":{"line":75,"column":0},"end":{"line":75,"column":24}},"75":{"start":{"line":76,"column":0},"end":{"line":76,"column":4}},"76":{"start":{"line":77,"column":0},"end":{"line":77,"column":37}},"78":{"start":{"line":79,"column":0},"end":{"line":79,"column":28}},"79":{"start":{"line":80,"column":0},"end":{"line":80,"column":36}},"80":{"start":{"line":81,"column":0},"end":{"line":81,"column":28}},"81":{"start":{"line":82,"column":0},"end":{"line":82,"column":28}},"83":{"start":{"line":84,"column":0},"end":{"line":84,"column":35}},"84":{"start":{"line":85,"column":0},"end":{"line":85,"column":15}},"86":{"start":{"line":87,"column":0},"end":{"line":87,"column":36}},"87":{"start":{"line":88,"column":0},"end":{"line":88,"column":16}},"89":{"start":{"line":90,"column":0},"end":{"line":90,"column":37}},"90":{"start":{"line":91,"column":0},"end":{"line":91,"column":20}},"92":{"start":{"line":93,"column":0},"end":{"line":93,"column":12}},"93":{"start":{"line":94,"column":0},"end":{"line":94,"column":26}},"94":{"start":{"line":95,"column":0},"end":{"line":95,"column":12}},"95":{"start":{"line":96,"column":0},"end":{"line":96,"column":26}},"96":{"start":{"line":97,"column":0},"end":{"line":97,"column":1}},"98":{"start":{"line":99,"column":0},"end":{"line":99,"column":21}}},"s":{"1":1,"13":1,"14":1,"15":1,"16":1,"18":1,"19":1,"20":1,"21":1,"22":1,"23":1,"24":1,"25":1,"26":1,"27":1,"28":1,"29":1,"30":1,"31":1,"32":1,"33":1,"34":1,"35":1,"36":1,"37":1,"38":1,"39":1,"40":1,"41":1,"42":1,"43":1,"44":1,"45":1,"46":1,"47":1,"48":1,"49":1,"50":1,"52":1,"53":1,"54":1,"55":1,"56":1,"58":1,"59":1,"60":1,"62":1,"63":1,"64":1,"65":1,"66":1,"67":1,"68":1,"69":1,"70":1,"71":1,"72":1,"73":1,"74":1,"75":1,"76":1,"78":1,"79":1,"80":1,"81":1,"83":1,"84":1,"86":1,"87":1,"89":1,"90":1,"92":1,"93":1,"94":1,"95":1,"96":1,"98":1},"branchMap":{},"b":{},"fnMap":{},"f":{}} +,"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/src/models/product.ts": {"path":"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/src/models/product.ts","all":false,"statementMap":{"1":{"start":{"line":2,"column":0},"end":{"line":2,"column":8}},"14":{"start":{"line":15,"column":0},"end":{"line":15,"column":26}},"15":{"start":{"line":16,"column":0},"end":{"line":16,"column":40}},"16":{"start":{"line":17,"column":0},"end":{"line":17,"column":28}},"18":{"start":{"line":19,"column":0},"end":{"line":19,"column":8}},"19":{"start":{"line":20,"column":0},"end":{"line":20,"column":19}},"20":{"start":{"line":21,"column":0},"end":{"line":21,"column":24}},"21":{"start":{"line":22,"column":0},"end":{"line":22,"column":23}},"22":{"start":{"line":23,"column":0},"end":{"line":23,"column":2}},"23":{"start":{"line":24,"column":0},"end":{"line":24,"column":48}},"24":{"start":{"line":25,"column":0},"end":{"line":25,"column":11}},"25":{"start":{"line":26,"column":0},"end":{"line":26,"column":21}},"26":{"start":{"line":27,"column":0},"end":{"line":27,"column":24}},"27":{"start":{"line":28,"column":0},"end":{"line":28,"column":34}},"28":{"start":{"line":29,"column":0},"end":{"line":29,"column":21}},"29":{"start":{"line":30,"column":0},"end":{"line":30,"column":4}},"30":{"start":{"line":31,"column":0},"end":{"line":31,"column":29}},"32":{"start":{"line":33,"column":0},"end":{"line":33,"column":11}},"33":{"start":{"line":34,"column":0},"end":{"line":34,"column":26}},"34":{"start":{"line":35,"column":0},"end":{"line":35,"column":4}},"35":{"start":{"line":36,"column":0},"end":{"line":36,"column":31}},"37":{"start":{"line":38,"column":0},"end":{"line":38,"column":11}},"38":{"start":{"line":39,"column":0},"end":{"line":39,"column":26}},"39":{"start":{"line":40,"column":0},"end":{"line":40,"column":4}},"40":{"start":{"line":41,"column":0},"end":{"line":41,"column":30}},"42":{"start":{"line":43,"column":0},"end":{"line":43,"column":11}},"43":{"start":{"line":44,"column":0},"end":{"line":44,"column":26}},"44":{"start":{"line":45,"column":0},"end":{"line":45,"column":4}},"45":{"start":{"line":46,"column":0},"end":{"line":46,"column":29}},"47":{"start":{"line":48,"column":0},"end":{"line":48,"column":11}},"48":{"start":{"line":49,"column":0},"end":{"line":49,"column":25}},"49":{"start":{"line":50,"column":0},"end":{"line":50,"column":4}},"50":{"start":{"line":51,"column":0},"end":{"line":51,"column":25}},"52":{"start":{"line":53,"column":0},"end":{"line":53,"column":11}},"53":{"start":{"line":54,"column":0},"end":{"line":54,"column":26}},"54":{"start":{"line":55,"column":0},"end":{"line":55,"column":4}},"55":{"start":{"line":56,"column":0},"end":{"line":56,"column":28}},"57":{"start":{"line":58,"column":0},"end":{"line":58,"column":11}},"58":{"start":{"line":59,"column":0},"end":{"line":59,"column":26}},"59":{"start":{"line":60,"column":0},"end":{"line":60,"column":4}},"60":{"start":{"line":61,"column":0},"end":{"line":61,"column":31}},"62":{"start":{"line":63,"column":0},"end":{"line":63,"column":11}},"63":{"start":{"line":64,"column":0},"end":{"line":64,"column":27}},"64":{"start":{"line":65,"column":0},"end":{"line":65,"column":4}},"65":{"start":{"line":66,"column":0},"end":{"line":66,"column":30}},"67":{"start":{"line":68,"column":0},"end":{"line":68,"column":25}},"68":{"start":{"line":69,"column":0},"end":{"line":69,"column":11}},"69":{"start":{"line":70,"column":0},"end":{"line":70,"column":24}},"70":{"start":{"line":71,"column":0},"end":{"line":71,"column":4}},"71":{"start":{"line":72,"column":0},"end":{"line":72,"column":26}},"73":{"start":{"line":74,"column":0},"end":{"line":74,"column":24}},"74":{"start":{"line":75,"column":0},"end":{"line":75,"column":14}},"76":{"start":{"line":77,"column":0},"end":{"line":77,"column":29}},"77":{"start":{"line":78,"column":0},"end":{"line":78,"column":30}},"79":{"start":{"line":80,"column":0},"end":{"line":80,"column":23}},"80":{"start":{"line":81,"column":0},"end":{"line":81,"column":18}},"82":{"start":{"line":83,"column":0},"end":{"line":83,"column":12}},"83":{"start":{"line":84,"column":0},"end":{"line":84,"column":26}},"84":{"start":{"line":85,"column":0},"end":{"line":85,"column":12}},"85":{"start":{"line":86,"column":0},"end":{"line":86,"column":26}},"86":{"start":{"line":87,"column":0},"end":{"line":87,"column":1}},"88":{"start":{"line":89,"column":0},"end":{"line":89,"column":23}}},"s":{"1":1,"14":1,"15":1,"16":1,"18":1,"19":1,"20":1,"21":1,"22":1,"23":1,"24":1,"25":1,"26":1,"27":1,"28":1,"29":1,"30":1,"32":1,"33":1,"34":1,"35":1,"37":1,"38":1,"39":1,"40":1,"42":1,"43":1,"44":1,"45":1,"47":1,"48":1,"49":1,"50":1,"52":1,"53":1,"54":1,"55":1,"57":1,"58":1,"59":1,"60":1,"62":1,"63":1,"64":1,"65":1,"67":1,"68":1,"69":1,"70":1,"71":1,"73":1,"74":1,"76":1,"77":1,"79":1,"80":1,"82":1,"83":1,"84":1,"85":1,"86":1,"88":1},"branchMap":{},"b":{},"fnMap":{},"f":{}} +,"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/src/models/role.ts": {"path":"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/src/models/role.ts","all":false,"statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":79}},"2":{"start":{"line":3,"column":0},"end":{"line":3,"column":26}},"4":{"start":{"line":5,"column":0},"end":{"line":5,"column":8}},"5":{"start":{"line":6,"column":0},"end":{"line":6,"column":19}},"6":{"start":{"line":7,"column":0},"end":{"line":7,"column":21}},"7":{"start":{"line":8,"column":0},"end":{"line":8,"column":20}},"8":{"start":{"line":9,"column":0},"end":{"line":9,"column":2}},"9":{"start":{"line":10,"column":0},"end":{"line":10,"column":42}},"10":{"start":{"line":11,"column":0},"end":{"line":11,"column":11}},"11":{"start":{"line":12,"column":0},"end":{"line":12,"column":21}},"12":{"start":{"line":13,"column":0},"end":{"line":13,"column":24}},"13":{"start":{"line":14,"column":0},"end":{"line":14,"column":34}},"14":{"start":{"line":15,"column":0},"end":{"line":15,"column":21}},"15":{"start":{"line":16,"column":0},"end":{"line":16,"column":4}},"16":{"start":{"line":17,"column":0},"end":{"line":17,"column":21}},"18":{"start":{"line":19,"column":0},"end":{"line":19,"column":11}},"19":{"start":{"line":20,"column":0},"end":{"line":20,"column":26}},"20":{"start":{"line":21,"column":0},"end":{"line":21,"column":4}},"21":{"start":{"line":22,"column":0},"end":{"line":22,"column":39}},"23":{"start":{"line":24,"column":0},"end":{"line":24,"column":32}},"24":{"start":{"line":25,"column":0},"end":{"line":25,"column":16}},"25":{"start":{"line":26,"column":0},"end":{"line":26,"column":1}},"27":{"start":{"line":28,"column":0},"end":{"line":28,"column":20}}},"s":{"0":1,"2":1,"4":1,"5":1,"6":1,"7":1,"8":1,"9":1,"10":1,"11":1,"12":1,"13":1,"14":1,"15":1,"16":1,"18":1,"19":1,"20":1,"21":1,"23":1,"24":1,"25":1,"27":1},"branchMap":{},"b":{},"fnMap":{},"f":{}} +,"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/src/models/transaction.ts": {"path":"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/src/models/transaction.ts","all":false,"statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":8}},"10":{"start":{"line":11,"column":0},"end":{"line":11,"column":28}},"13":{"start":{"line":14,"column":0},"end":{"line":14,"column":8}},"14":{"start":{"line":15,"column":0},"end":{"line":15,"column":19}},"15":{"start":{"line":16,"column":0},"end":{"line":16,"column":28}},"16":{"start":{"line":17,"column":0},"end":{"line":17,"column":27}},"17":{"start":{"line":18,"column":0},"end":{"line":18,"column":2}},"18":{"start":{"line":19,"column":0},"end":{"line":19,"column":56}},"19":{"start":{"line":20,"column":0},"end":{"line":20,"column":11}},"20":{"start":{"line":21,"column":0},"end":{"line":21,"column":21}},"21":{"start":{"line":22,"column":0},"end":{"line":22,"column":24}},"22":{"start":{"line":23,"column":0},"end":{"line":23,"column":34}},"23":{"start":{"line":24,"column":0},"end":{"line":24,"column":21}},"24":{"start":{"line":25,"column":0},"end":{"line":25,"column":4}},"25":{"start":{"line":26,"column":0},"end":{"line":26,"column":29}},"26":{"start":{"line":27,"column":0},"end":{"line":27,"column":11}},"27":{"start":{"line":28,"column":0},"end":{"line":28,"column":27}},"28":{"start":{"line":29,"column":0},"end":{"line":29,"column":4}},"29":{"start":{"line":30,"column":0},"end":{"line":30,"column":26}},"30":{"start":{"line":31,"column":0},"end":{"line":31,"column":11}},"31":{"start":{"line":32,"column":0},"end":{"line":32,"column":60}},"32":{"start":{"line":33,"column":0},"end":{"line":33,"column":4}},"33":{"start":{"line":34,"column":0},"end":{"line":34,"column":25}},"34":{"start":{"line":35,"column":0},"end":{"line":35,"column":11}},"35":{"start":{"line":36,"column":0},"end":{"line":36,"column":45}},"36":{"start":{"line":37,"column":0},"end":{"line":37,"column":4}},"37":{"start":{"line":38,"column":0},"end":{"line":38,"column":25}},"38":{"start":{"line":39,"column":0},"end":{"line":39,"column":11}},"39":{"start":{"line":40,"column":0},"end":{"line":40,"column":78}},"40":{"start":{"line":41,"column":0},"end":{"line":41,"column":4}},"41":{"start":{"line":42,"column":0},"end":{"line":42,"column":28}},"42":{"start":{"line":43,"column":0},"end":{"line":43,"column":11}},"43":{"start":{"line":44,"column":0},"end":{"line":44,"column":24}},"44":{"start":{"line":45,"column":0},"end":{"line":45,"column":4}},"45":{"start":{"line":46,"column":0},"end":{"line":46,"column":36}},"46":{"start":{"line":47,"column":0},"end":{"line":47,"column":11}},"47":{"start":{"line":48,"column":0},"end":{"line":48,"column":26}},"48":{"start":{"line":49,"column":0},"end":{"line":49,"column":4}},"49":{"start":{"line":50,"column":0},"end":{"line":50,"column":27}},"50":{"start":{"line":51,"column":0},"end":{"line":51,"column":26}},"51":{"start":{"line":52,"column":0},"end":{"line":52,"column":11}},"52":{"start":{"line":53,"column":0},"end":{"line":53,"column":24}},"53":{"start":{"line":54,"column":0},"end":{"line":54,"column":4}},"54":{"start":{"line":55,"column":0},"end":{"line":55,"column":27}},"56":{"start":{"line":57,"column":0},"end":{"line":57,"column":25}},"57":{"start":{"line":58,"column":0},"end":{"line":58,"column":16}},"58":{"start":{"line":59,"column":0},"end":{"line":59,"column":12}},"59":{"start":{"line":60,"column":0},"end":{"line":60,"column":26}},"60":{"start":{"line":61,"column":0},"end":{"line":61,"column":12}},"61":{"start":{"line":62,"column":0},"end":{"line":62,"column":26}},"62":{"start":{"line":63,"column":0},"end":{"line":63,"column":1}},"64":{"start":{"line":65,"column":0},"end":{"line":65,"column":27}}},"s":{"0":1,"10":1,"13":1,"14":1,"15":1,"16":1,"17":1,"18":1,"19":1,"20":1,"21":1,"22":1,"23":1,"24":1,"25":1,"26":1,"27":1,"28":1,"29":1,"30":1,"31":1,"32":1,"33":1,"34":1,"35":1,"36":1,"37":1,"38":1,"39":1,"40":1,"41":1,"42":1,"43":1,"44":1,"45":1,"46":1,"47":1,"48":1,"49":1,"50":1,"51":1,"52":1,"53":1,"54":1,"56":1,"57":1,"58":1,"59":1,"60":1,"61":1,"62":1,"64":1},"branchMap":{},"b":{},"fnMap":{},"f":{}} +,"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/src/models/user.ts": {"path":"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/src/models/user.ts","all":false,"statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":8}},"12":{"start":{"line":13,"column":0},"end":{"line":13,"column":26}},"13":{"start":{"line":14,"column":0},"end":{"line":14,"column":28}},"14":{"start":{"line":15,"column":0},"end":{"line":15,"column":40}},"15":{"start":{"line":16,"column":0},"end":{"line":16,"column":32}},"17":{"start":{"line":18,"column":0},"end":{"line":18,"column":8}},"18":{"start":{"line":19,"column":0},"end":{"line":19,"column":19}},"19":{"start":{"line":20,"column":0},"end":{"line":20,"column":21}},"20":{"start":{"line":21,"column":0},"end":{"line":21,"column":20}},"21":{"start":{"line":22,"column":0},"end":{"line":22,"column":2}},"22":{"start":{"line":23,"column":0},"end":{"line":23,"column":42}},"23":{"start":{"line":24,"column":0},"end":{"line":24,"column":11}},"24":{"start":{"line":25,"column":0},"end":{"line":25,"column":21}},"25":{"start":{"line":26,"column":0},"end":{"line":26,"column":24}},"26":{"start":{"line":27,"column":0},"end":{"line":27,"column":34}},"27":{"start":{"line":28,"column":0},"end":{"line":28,"column":21}},"28":{"start":{"line":29,"column":0},"end":{"line":29,"column":4}},"29":{"start":{"line":30,"column":0},"end":{"line":30,"column":21}},"30":{"start":{"line":31,"column":0},"end":{"line":31,"column":11}},"31":{"start":{"line":32,"column":0},"end":{"line":32,"column":26}},"32":{"start":{"line":33,"column":0},"end":{"line":33,"column":4}},"33":{"start":{"line":34,"column":0},"end":{"line":34,"column":28}},"34":{"start":{"line":35,"column":0},"end":{"line":35,"column":11}},"35":{"start":{"line":36,"column":0},"end":{"line":36,"column":26}},"36":{"start":{"line":37,"column":0},"end":{"line":37,"column":4}},"37":{"start":{"line":38,"column":0},"end":{"line":38,"column":27}},"38":{"start":{"line":39,"column":0},"end":{"line":39,"column":11}},"39":{"start":{"line":40,"column":0},"end":{"line":40,"column":26}},"40":{"start":{"line":41,"column":0},"end":{"line":41,"column":4}},"41":{"start":{"line":42,"column":0},"end":{"line":42,"column":24}},"42":{"start":{"line":43,"column":0},"end":{"line":43,"column":11}},"43":{"start":{"line":44,"column":0},"end":{"line":44,"column":26}},"44":{"start":{"line":45,"column":0},"end":{"line":45,"column":4}},"45":{"start":{"line":46,"column":0},"end":{"line":46,"column":26}},"46":{"start":{"line":47,"column":0},"end":{"line":47,"column":11}},"47":{"start":{"line":48,"column":0},"end":{"line":48,"column":24}},"48":{"start":{"line":49,"column":0},"end":{"line":49,"column":21}},"49":{"start":{"line":50,"column":0},"end":{"line":50,"column":4}},"50":{"start":{"line":51,"column":0},"end":{"line":51,"column":32}},"51":{"start":{"line":52,"column":0},"end":{"line":52,"column":11}},"52":{"start":{"line":53,"column":0},"end":{"line":53,"column":26}},"53":{"start":{"line":54,"column":0},"end":{"line":54,"column":4}},"54":{"start":{"line":55,"column":0},"end":{"line":55,"column":26}},"55":{"start":{"line":56,"column":0},"end":{"line":56,"column":11}},"56":{"start":{"line":57,"column":0},"end":{"line":57,"column":26}},"57":{"start":{"line":58,"column":0},"end":{"line":58,"column":4}},"58":{"start":{"line":59,"column":0},"end":{"line":59,"column":27}},"59":{"start":{"line":60,"column":0},"end":{"line":60,"column":11}},"60":{"start":{"line":61,"column":0},"end":{"line":61,"column":26}},"61":{"start":{"line":62,"column":0},"end":{"line":62,"column":4}},"62":{"start":{"line":63,"column":0},"end":{"line":63,"column":27}},"63":{"start":{"line":64,"column":0},"end":{"line":64,"column":11}},"64":{"start":{"line":65,"column":0},"end":{"line":65,"column":26}},"65":{"start":{"line":66,"column":0},"end":{"line":66,"column":4}},"66":{"start":{"line":67,"column":0},"end":{"line":67,"column":29}},"67":{"start":{"line":68,"column":0},"end":{"line":68,"column":11}},"68":{"start":{"line":69,"column":0},"end":{"line":69,"column":26}},"69":{"start":{"line":70,"column":0},"end":{"line":70,"column":4}},"70":{"start":{"line":71,"column":0},"end":{"line":71,"column":27}},"71":{"start":{"line":72,"column":0},"end":{"line":72,"column":11}},"72":{"start":{"line":73,"column":0},"end":{"line":73,"column":26}},"73":{"start":{"line":74,"column":0},"end":{"line":74,"column":4}},"74":{"start":{"line":75,"column":0},"end":{"line":75,"column":34}},"75":{"start":{"line":76,"column":0},"end":{"line":76,"column":11}},"76":{"start":{"line":77,"column":0},"end":{"line":77,"column":26}},"77":{"start":{"line":78,"column":0},"end":{"line":78,"column":4}},"78":{"start":{"line":79,"column":0},"end":{"line":79,"column":39}},"80":{"start":{"line":81,"column":0},"end":{"line":81,"column":11}},"81":{"start":{"line":82,"column":0},"end":{"line":82,"column":27}},"82":{"start":{"line":83,"column":0},"end":{"line":83,"column":4}},"83":{"start":{"line":84,"column":0},"end":{"line":84,"column":23}},"84":{"start":{"line":85,"column":0},"end":{"line":85,"column":11}},"85":{"start":{"line":86,"column":0},"end":{"line":86,"column":27}},"86":{"start":{"line":87,"column":0},"end":{"line":87,"column":4}},"87":{"start":{"line":88,"column":0},"end":{"line":88,"column":32}},"88":{"start":{"line":89,"column":0},"end":{"line":89,"column":25}},"89":{"start":{"line":90,"column":0},"end":{"line":90,"column":11}},"90":{"start":{"line":91,"column":0},"end":{"line":91,"column":24}},"91":{"start":{"line":92,"column":0},"end":{"line":92,"column":4}},"92":{"start":{"line":93,"column":0},"end":{"line":93,"column":18}},"94":{"start":{"line":95,"column":0},"end":{"line":95,"column":24}},"95":{"start":{"line":96,"column":0},"end":{"line":96,"column":14}},"97":{"start":{"line":98,"column":0},"end":{"line":98,"column":34}},"98":{"start":{"line":99,"column":0},"end":{"line":99,"column":26}},"100":{"start":{"line":101,"column":0},"end":{"line":101,"column":35}},"101":{"start":{"line":102,"column":0},"end":{"line":102,"column":27}},"103":{"start":{"line":104,"column":0},"end":{"line":104,"column":35}},"104":{"start":{"line":105,"column":0},"end":{"line":105,"column":22}},"106":{"start":{"line":107,"column":0},"end":{"line":107,"column":39}},"107":{"start":{"line":108,"column":0},"end":{"line":108,"column":31}},"109":{"start":{"line":110,"column":0},"end":{"line":110,"column":12}},"110":{"start":{"line":111,"column":0},"end":{"line":111,"column":26}},"111":{"start":{"line":112,"column":0},"end":{"line":112,"column":12}},"112":{"start":{"line":113,"column":0},"end":{"line":113,"column":26}},"113":{"start":{"line":114,"column":0},"end":{"line":114,"column":1}},"115":{"start":{"line":116,"column":0},"end":{"line":116,"column":20}}},"s":{"0":1,"12":1,"13":1,"14":1,"15":1,"17":1,"18":1,"19":1,"20":1,"21":1,"22":1,"23":1,"24":1,"25":1,"26":1,"27":1,"28":1,"29":1,"30":1,"31":1,"32":1,"33":1,"34":1,"35":1,"36":1,"37":1,"38":1,"39":1,"40":1,"41":1,"42":1,"43":1,"44":1,"45":1,"46":1,"47":1,"48":1,"49":1,"50":1,"51":1,"52":1,"53":1,"54":1,"55":1,"56":1,"57":1,"58":1,"59":1,"60":1,"61":1,"62":1,"63":1,"64":1,"65":1,"66":1,"67":1,"68":1,"69":1,"70":1,"71":1,"72":1,"73":1,"74":1,"75":1,"76":1,"77":1,"78":1,"80":1,"81":1,"82":1,"83":1,"84":1,"85":1,"86":1,"87":1,"88":1,"89":1,"90":1,"91":1,"92":1,"94":1,"95":1,"97":1,"98":1,"100":1,"101":1,"103":1,"104":1,"106":1,"107":1,"109":1,"110":1,"111":1,"112":1,"113":1,"115":1},"branchMap":{"0":{"type":"branch","line":96,"loc":{"start":{"line":96,"column":2},"end":{"line":96,"column":14}},"locations":[{"start":{"line":96,"column":2},"end":{"line":96,"column":14}}]},"1":{"type":"branch","line":99,"loc":{"start":{"line":99,"column":2},"end":{"line":99,"column":26}},"locations":[{"start":{"line":99,"column":2},"end":{"line":99,"column":26}}]},"2":{"type":"branch","line":102,"loc":{"start":{"line":102,"column":2},"end":{"line":102,"column":27}},"locations":[{"start":{"line":102,"column":2},"end":{"line":102,"column":27}}]},"3":{"type":"branch","line":105,"loc":{"start":{"line":105,"column":2},"end":{"line":105,"column":22}},"locations":[{"start":{"line":105,"column":2},"end":{"line":105,"column":22}}]},"4":{"type":"branch","line":108,"loc":{"start":{"line":108,"column":2},"end":{"line":108,"column":31}},"locations":[{"start":{"line":108,"column":2},"end":{"line":108,"column":31}}]}},"b":{"0":[1],"1":[1],"2":[1],"3":[1],"4":[1]},"fnMap":{},"f":{}} +,"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/src/models/userotpcode.ts": {"path":"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/src/models/userotpcode.ts","all":false,"statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":8}},"10":{"start":{"line":11,"column":0},"end":{"line":11,"column":26}},"11":{"start":{"line":12,"column":0},"end":{"line":12,"column":8}},"12":{"start":{"line":13,"column":0},"end":{"line":13,"column":19}},"13":{"start":{"line":14,"column":0},"end":{"line":14,"column":28}},"14":{"start":{"line":15,"column":0},"end":{"line":15,"column":27}},"15":{"start":{"line":16,"column":0},"end":{"line":16,"column":2}},"16":{"start":{"line":17,"column":0},"end":{"line":17,"column":52}},"17":{"start":{"line":18,"column":0},"end":{"line":18,"column":11}},"18":{"start":{"line":19,"column":0},"end":{"line":19,"column":21}},"19":{"start":{"line":20,"column":0},"end":{"line":20,"column":24}},"20":{"start":{"line":21,"column":0},"end":{"line":21,"column":34}},"21":{"start":{"line":22,"column":0},"end":{"line":22,"column":21}},"22":{"start":{"line":23,"column":0},"end":{"line":23,"column":4}},"23":{"start":{"line":24,"column":0},"end":{"line":24,"column":21}},"24":{"start":{"line":25,"column":0},"end":{"line":25,"column":11}},"25":{"start":{"line":26,"column":0},"end":{"line":26,"column":26}},"26":{"start":{"line":27,"column":0},"end":{"line":27,"column":4}},"27":{"start":{"line":28,"column":0},"end":{"line":28,"column":25}},"28":{"start":{"line":29,"column":0},"end":{"line":29,"column":11}},"29":{"start":{"line":30,"column":0},"end":{"line":30,"column":24}},"30":{"start":{"line":31,"column":0},"end":{"line":31,"column":4}},"31":{"start":{"line":32,"column":0},"end":{"line":32,"column":18}},"32":{"start":{"line":33,"column":0},"end":{"line":33,"column":11}},"33":{"start":{"line":34,"column":0},"end":{"line":34,"column":26}},"34":{"start":{"line":35,"column":0},"end":{"line":35,"column":4}},"35":{"start":{"line":36,"column":0},"end":{"line":36,"column":22}},"36":{"start":{"line":37,"column":0},"end":{"line":37,"column":11}},"37":{"start":{"line":38,"column":0},"end":{"line":38,"column":26}},"38":{"start":{"line":39,"column":0},"end":{"line":39,"column":4}},"39":{"start":{"line":40,"column":0},"end":{"line":40,"column":28}},"40":{"start":{"line":41,"column":0},"end":{"line":41,"column":12}},"41":{"start":{"line":42,"column":0},"end":{"line":42,"column":26}},"42":{"start":{"line":43,"column":0},"end":{"line":43,"column":12}},"43":{"start":{"line":44,"column":0},"end":{"line":44,"column":26}},"44":{"start":{"line":45,"column":0},"end":{"line":45,"column":1}},"46":{"start":{"line":47,"column":0},"end":{"line":47,"column":27}}},"s":{"0":1,"10":1,"11":1,"12":1,"13":1,"14":1,"15":1,"16":1,"17":1,"18":1,"19":1,"20":1,"21":1,"22":1,"23":1,"24":1,"25":1,"26":1,"27":1,"28":1,"29":1,"30":1,"31":1,"32":1,"33":1,"34":1,"35":1,"36":1,"37":1,"38":1,"39":1,"40":1,"41":1,"42":1,"43":1,"44":1,"46":1},"branchMap":{},"b":{},"fnMap":{},"f":{}} +,"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/src/routes/auth.routes.ts": {"path":"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/src/routes/auth.routes.ts","all":false,"statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":42}},"1":{"start":{"line":2,"column":0},"end":{"line":2,"column":8}},"11":{"start":{"line":12,"column":0},"end":{"line":12,"column":44}},"53":{"start":{"line":54,"column":0},"end":{"line":54,"column":40}},"82":{"start":{"line":83,"column":0},"end":{"line":83,"column":41}},"122":{"start":{"line":123,"column":0},"end":{"line":123,"column":49}},"175":{"start":{"line":176,"column":0},"end":{"line":176,"column":33}},"202":{"start":{"line":203,"column":0},"end":{"line":203,"column":41}},"295":{"start":{"line":296,"column":0},"end":{"line":296,"column":46}},"329":{"start":{"line":330,"column":0},"end":{"line":330,"column":47}},"331":{"start":{"line":332,"column":0},"end":{"line":332,"column":26}}},"s":{"0":1,"1":1,"11":1,"53":1,"82":1,"122":1,"175":1,"202":1,"295":1,"329":1,"331":1},"branchMap":{},"b":{},"fnMap":{},"f":{}} +,"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/src/routes/index.ts": {"path":"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/src/routes/index.ts","all":false,"statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":42}},"1":{"start":{"line":2,"column":0},"end":{"line":2,"column":39}},"2":{"start":{"line":3,"column":0},"end":{"line":3,"column":39}},"3":{"start":{"line":4,"column":0},"end":{"line":4,"column":45}},"4":{"start":{"line":5,"column":0},"end":{"line":5,"column":43}},"5":{"start":{"line":6,"column":0},"end":{"line":6,"column":41}},"6":{"start":{"line":7,"column":0},"end":{"line":7,"column":65}},"7":{"start":{"line":8,"column":0},"end":{"line":8,"column":55}},"9":{"start":{"line":10,"column":0},"end":{"line":10,"column":43}},"11":{"start":{"line":12,"column":0},"end":{"line":12,"column":48}},"12":{"start":{"line":13,"column":0},"end":{"line":13,"column":35}},"13":{"start":{"line":14,"column":0},"end":{"line":14,"column":36}},"14":{"start":{"line":15,"column":0},"end":{"line":15,"column":42}},"15":{"start":{"line":16,"column":0},"end":{"line":16,"column":40}},"16":{"start":{"line":17,"column":0},"end":{"line":17,"column":38}},"17":{"start":{"line":18,"column":0},"end":{"line":18,"column":55}},"18":{"start":{"line":19,"column":0},"end":{"line":19,"column":52}},"20":{"start":{"line":21,"column":0},"end":{"line":21,"column":25}}},"s":{"0":1,"1":1,"2":1,"3":1,"4":1,"5":1,"6":1,"7":1,"9":1,"11":1,"12":1,"13":1,"14":1,"15":1,"16":1,"17":1,"18":1,"20":1},"branchMap":{},"b":{},"fnMap":{},"f":{}} +,"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/src/routes/notification.routes.ts": {"path":"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/src/routes/notification.routes.ts","all":false,"statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":75}},"1":{"start":{"line":2,"column":0},"end":{"line":2,"column":49}},"2":{"start":{"line":3,"column":0},"end":{"line":3,"column":24}},"4":{"start":{"line":5,"column":0},"end":{"line":5,"column":8}},"11":{"start":{"line":12,"column":0},"end":{"line":12,"column":37}},"12":{"start":{"line":13,"column":0},"end":{"line":13,"column":27}},"13":{"start":{"line":14,"column":0},"end":{"line":14,"column":29}},"14":{"start":{"line":15,"column":0},"end":{"line":15,"column":3}},"16":{"start":{"line":17,"column":0},"end":{"line":17,"column":80}},"17":{"start":{"line":18,"column":0},"end":{"line":18,"column":56}},"18":{"start":{"line":19,"column":0},"end":{"line":19,"column":24}},"19":{"start":{"line":20,"column":0},"end":{"line":20,"column":80}},"20":{"start":{"line":21,"column":0},"end":{"line":21,"column":3}},"21":{"start":{"line":22,"column":0},"end":{"line":22,"column":9}},"22":{"start":{"line":23,"column":0},"end":{"line":23,"column":1}},"24":{"start":{"line":25,"column":0},"end":{"line":25,"column":52}},"26":{"start":{"line":27,"column":0},"end":{"line":27,"column":34}},"118":{"start":{"line":119,"column":0},"end":{"line":119,"column":24}},"119":{"start":{"line":120,"column":0},"end":{"line":120,"column":20}},"120":{"start":{"line":121,"column":0},"end":{"line":121,"column":23}},"121":{"start":{"line":122,"column":0},"end":{"line":122,"column":21}},"122":{"start":{"line":123,"column":0},"end":{"line":123,"column":2}},"196":{"start":{"line":197,"column":0},"end":{"line":197,"column":52}},"225":{"start":{"line":226,"column":0},"end":{"line":226,"column":43}},"256":{"start":{"line":257,"column":0},"end":{"line":257,"column":62}},"258":{"start":{"line":259,"column":0},"end":{"line":259,"column":34}}},"s":{"0":1,"1":1,"2":1,"4":1,"11":1,"12":1,"13":1,"14":1,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"24":1,"26":1,"118":1,"119":1,"120":1,"121":1,"122":1,"196":1,"225":1,"256":1,"258":1},"branchMap":{},"b":{},"fnMap":{"0":{"name":"validateNotification","decl":{"start":{"line":17,"column":0},"end":{"line":23,"column":1}},"loc":{"start":{"line":17,"column":0},"end":{"line":23,"column":1}},"line":17}},"f":{"0":0}} +,"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/src/routes/order.routes.ts": {"path":"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/src/routes/order.routes.ts","all":false,"statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":75}},"1":{"start":{"line":2,"column":0},"end":{"line":2,"column":49}},"2":{"start":{"line":3,"column":0},"end":{"line":3,"column":8}},"11":{"start":{"line":12,"column":0},"end":{"line":12,"column":59}},"12":{"start":{"line":13,"column":0},"end":{"line":13,"column":24}},"14":{"start":{"line":15,"column":0},"end":{"line":15,"column":30}},"15":{"start":{"line":16,"column":0},"end":{"line":16,"column":33}},"16":{"start":{"line":17,"column":0},"end":{"line":17,"column":28}},"17":{"start":{"line":18,"column":0},"end":{"line":18,"column":30}},"18":{"start":{"line":19,"column":0},"end":{"line":19,"column":28}},"19":{"start":{"line":20,"column":0},"end":{"line":20,"column":3}},"21":{"start":{"line":22,"column":0},"end":{"line":22,"column":73}},"22":{"start":{"line":23,"column":0},"end":{"line":23,"column":40}},"23":{"start":{"line":24,"column":0},"end":{"line":24,"column":16}},"24":{"start":{"line":25,"column":0},"end":{"line":25,"column":66}},"25":{"start":{"line":26,"column":0},"end":{"line":26,"column":5}},"26":{"start":{"line":27,"column":0},"end":{"line":27,"column":24}},"27":{"start":{"line":28,"column":0},"end":{"line":28,"column":80}},"28":{"start":{"line":29,"column":0},"end":{"line":29,"column":3}},"29":{"start":{"line":30,"column":0},"end":{"line":30,"column":9}},"30":{"start":{"line":31,"column":0},"end":{"line":31,"column":1}},"32":{"start":{"line":33,"column":0},"end":{"line":33,"column":45}},"34":{"start":{"line":35,"column":0},"end":{"line":35,"column":27}},"131":{"start":{"line":132,"column":0},"end":{"line":132,"column":51}},"162":{"start":{"line":163,"column":0},"end":{"line":163,"column":54}},"187":{"start":{"line":188,"column":0},"end":{"line":188,"column":43}},"229":{"start":{"line":230,"column":0},"end":{"line":230,"column":64}},"255":{"start":{"line":256,"column":0},"end":{"line":256,"column":51}},"257":{"start":{"line":258,"column":0},"end":{"line":258,"column":16}},"258":{"start":{"line":259,"column":0},"end":{"line":259,"column":23}},"259":{"start":{"line":260,"column":0},"end":{"line":260,"column":36}},"260":{"start":{"line":261,"column":0},"end":{"line":261,"column":24}},"261":{"start":{"line":262,"column":0},"end":{"line":262,"column":2}},"263":{"start":{"line":264,"column":0},"end":{"line":264,"column":57}},"265":{"start":{"line":266,"column":0},"end":{"line":266,"column":27}}},"s":{"0":1,"1":1,"2":1,"11":1,"12":1,"14":1,"15":1,"16":1,"17":1,"18":1,"19":1,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0,"30":0,"32":1,"34":1,"131":1,"162":1,"187":1,"229":1,"255":1,"257":1,"258":1,"259":1,"260":1,"261":1,"263":1,"265":1},"branchMap":{},"b":{},"fnMap":{"0":{"name":"validateOrder","decl":{"start":{"line":22,"column":0},"end":{"line":31,"column":1}},"loc":{"start":{"line":22,"column":0},"end":{"line":31,"column":1}},"line":22}},"f":{"0":0}} +,"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/src/routes/payment.collection.routes.ts": {"path":"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/src/routes/payment.collection.routes.ts","all":false,"statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":75}},"1":{"start":{"line":2,"column":0},"end":{"line":2,"column":49}},"2":{"start":{"line":3,"column":0},"end":{"line":3,"column":8}},"6":{"start":{"line":7,"column":0},"end":{"line":7,"column":24}},"8":{"start":{"line":9,"column":0},"end":{"line":9,"column":32}},"9":{"start":{"line":10,"column":0},"end":{"line":10,"column":30}},"10":{"start":{"line":11,"column":0},"end":{"line":11,"column":35}},"11":{"start":{"line":12,"column":0},"end":{"line":12,"column":30}},"12":{"start":{"line":13,"column":0},"end":{"line":13,"column":32}},"13":{"start":{"line":14,"column":0},"end":{"line":14,"column":28}},"14":{"start":{"line":15,"column":0},"end":{"line":15,"column":3}},"16":{"start":{"line":17,"column":0},"end":{"line":17,"column":75}},"17":{"start":{"line":18,"column":0},"end":{"line":18,"column":42}},"18":{"start":{"line":19,"column":0},"end":{"line":19,"column":16}},"19":{"start":{"line":20,"column":0},"end":{"line":20,"column":66}},"20":{"start":{"line":21,"column":0},"end":{"line":21,"column":78}},"21":{"start":{"line":22,"column":0},"end":{"line":22,"column":5}},"22":{"start":{"line":23,"column":0},"end":{"line":23,"column":24}},"23":{"start":{"line":24,"column":0},"end":{"line":24,"column":80}},"24":{"start":{"line":25,"column":0},"end":{"line":25,"column":3}},"25":{"start":{"line":26,"column":0},"end":{"line":26,"column":9}},"26":{"start":{"line":27,"column":0},"end":{"line":27,"column":1}},"28":{"start":{"line":29,"column":0},"end":{"line":29,"column":56}},"30":{"start":{"line":31,"column":0},"end":{"line":31,"column":72}},"167":{"start":{"line":168,"column":0},"end":{"line":168,"column":28}},"168":{"start":{"line":169,"column":0},"end":{"line":169,"column":39}},"169":{"start":{"line":170,"column":0},"end":{"line":170,"column":12}},"170":{"start":{"line":171,"column":0},"end":{"line":171,"column":18}},"171":{"start":{"line":172,"column":0},"end":{"line":172,"column":26}},"172":{"start":{"line":173,"column":0},"end":{"line":173,"column":2}},"174":{"start":{"line":175,"column":0},"end":{"line":175,"column":38}}},"s":{"0":1,"1":1,"2":1,"6":1,"8":1,"9":1,"10":1,"11":1,"12":1,"13":1,"14":1,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"28":1,"30":1,"167":1,"168":1,"169":1,"170":1,"171":1,"172":1,"174":1},"branchMap":{},"b":{},"fnMap":{"0":{"name":"validatePayment","decl":{"start":{"line":17,"column":0},"end":{"line":27,"column":1}},"loc":{"start":{"line":17,"column":0},"end":{"line":27,"column":1}},"line":17}},"f":{"0":0}} +,"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/src/routes/product.routes.ts": {"path":"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/src/routes/product.routes.ts","all":false,"statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":75}},"1":{"start":{"line":2,"column":0},"end":{"line":2,"column":49}},"2":{"start":{"line":3,"column":0},"end":{"line":3,"column":59}},"3":{"start":{"line":4,"column":0},"end":{"line":4,"column":8}},"10":{"start":{"line":11,"column":0},"end":{"line":11,"column":79}},"11":{"start":{"line":12,"column":0},"end":{"line":12,"column":24}},"13":{"start":{"line":14,"column":0},"end":{"line":14,"column":32}},"14":{"start":{"line":15,"column":0},"end":{"line":15,"column":33}},"15":{"start":{"line":16,"column":0},"end":{"line":16,"column":32}},"16":{"start":{"line":17,"column":0},"end":{"line":17,"column":31}},"17":{"start":{"line":18,"column":0},"end":{"line":18,"column":27}},"18":{"start":{"line":19,"column":0},"end":{"line":19,"column":37}},"19":{"start":{"line":20,"column":0},"end":{"line":20,"column":36}},"20":{"start":{"line":21,"column":0},"end":{"line":21,"column":3}},"22":{"start":{"line":23,"column":0},"end":{"line":23,"column":75}},"23":{"start":{"line":24,"column":0},"end":{"line":24,"column":24}},"25":{"start":{"line":26,"column":0},"end":{"line":26,"column":42}},"26":{"start":{"line":27,"column":0},"end":{"line":27,"column":12}},"27":{"start":{"line":28,"column":0},"end":{"line":28,"column":55}},"28":{"start":{"line":29,"column":0},"end":{"line":29,"column":68}},"29":{"start":{"line":30,"column":0},"end":{"line":30,"column":5}},"30":{"start":{"line":31,"column":0},"end":{"line":31,"column":24}},"31":{"start":{"line":32,"column":0},"end":{"line":32,"column":80}},"32":{"start":{"line":33,"column":0},"end":{"line":33,"column":3}},"33":{"start":{"line":34,"column":0},"end":{"line":34,"column":9}},"34":{"start":{"line":35,"column":0},"end":{"line":35,"column":1}},"36":{"start":{"line":37,"column":0},"end":{"line":37,"column":47}},"38":{"start":{"line":39,"column":0},"end":{"line":39,"column":29}},"168":{"start":{"line":169,"column":0},"end":{"line":169,"column":44}},"192":{"start":{"line":193,"column":0},"end":{"line":193,"column":45}},"218":{"start":{"line":219,"column":0},"end":{"line":219,"column":53}},"273":{"start":{"line":274,"column":0},"end":{"line":274,"column":19}},"274":{"start":{"line":275,"column":0},"end":{"line":275,"column":9}},"275":{"start":{"line":276,"column":0},"end":{"line":276,"column":35}},"276":{"start":{"line":277,"column":0},"end":{"line":277,"column":18}},"277":{"start":{"line":278,"column":0},"end":{"line":278,"column":16}},"278":{"start":{"line":279,"column":0},"end":{"line":279,"column":2}},"332":{"start":{"line":333,"column":0},"end":{"line":333,"column":18}},"333":{"start":{"line":334,"column":0},"end":{"line":334,"column":16}},"334":{"start":{"line":335,"column":0},"end":{"line":335,"column":35}},"335":{"start":{"line":336,"column":0},"end":{"line":336,"column":18}},"336":{"start":{"line":337,"column":0},"end":{"line":337,"column":16}},"337":{"start":{"line":338,"column":0},"end":{"line":338,"column":2}},"363":{"start":{"line":364,"column":0},"end":{"line":364,"column":51}},"365":{"start":{"line":366,"column":0},"end":{"line":366,"column":29}}},"s":{"0":1,"1":1,"2":1,"3":1,"10":1,"11":1,"13":1,"14":1,"15":1,"16":1,"17":1,"18":1,"19":1,"20":1,"22":0,"23":0,"25":0,"26":0,"27":0,"28":0,"29":0,"30":0,"31":0,"32":0,"33":0,"34":0,"36":1,"38":1,"168":1,"192":1,"218":1,"273":1,"274":1,"275":1,"276":1,"277":1,"278":1,"332":1,"333":1,"334":1,"335":1,"336":1,"337":1,"363":1,"365":1},"branchMap":{},"b":{},"fnMap":{"0":{"name":"validateProduct","decl":{"start":{"line":23,"column":0},"end":{"line":35,"column":1}},"loc":{"start":{"line":23,"column":0},"end":{"line":35,"column":1}},"line":23}},"f":{"0":0}} +,"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/src/routes/review.routes.ts": {"path":"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/src/routes/review.routes.ts","all":false,"statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":75}},"1":{"start":{"line":2,"column":0},"end":{"line":2,"column":49}},"2":{"start":{"line":3,"column":0},"end":{"line":3,"column":8}},"9":{"start":{"line":10,"column":0},"end":{"line":10,"column":24}},"11":{"start":{"line":12,"column":0},"end":{"line":12,"column":31}},"12":{"start":{"line":13,"column":0},"end":{"line":13,"column":29}},"13":{"start":{"line":14,"column":0},"end":{"line":14,"column":35}},"14":{"start":{"line":15,"column":0},"end":{"line":15,"column":3}},"16":{"start":{"line":17,"column":0},"end":{"line":17,"column":74}},"17":{"start":{"line":18,"column":0},"end":{"line":18,"column":41}},"18":{"start":{"line":19,"column":0},"end":{"line":19,"column":16}},"19":{"start":{"line":20,"column":0},"end":{"line":20,"column":66}},"20":{"start":{"line":21,"column":0},"end":{"line":21,"column":5}},"21":{"start":{"line":22,"column":0},"end":{"line":22,"column":24}},"22":{"start":{"line":23,"column":0},"end":{"line":23,"column":80}},"23":{"start":{"line":24,"column":0},"end":{"line":24,"column":3}},"24":{"start":{"line":25,"column":0},"end":{"line":25,"column":9}},"25":{"start":{"line":26,"column":0},"end":{"line":26,"column":1}},"27":{"start":{"line":28,"column":0},"end":{"line":28,"column":46}},"131":{"start":{"line":132,"column":0},"end":{"line":132,"column":54}},"163":{"start":{"line":164,"column":0},"end":{"line":164,"column":62}},"207":{"start":{"line":208,"column":0},"end":{"line":208,"column":76}},"245":{"start":{"line":246,"column":0},"end":{"line":246,"column":68}},"272":{"start":{"line":273,"column":0},"end":{"line":273,"column":69}},"274":{"start":{"line":275,"column":0},"end":{"line":275,"column":28}}},"s":{"0":1,"1":1,"2":1,"9":1,"11":1,"12":1,"13":1,"14":1,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"27":1,"131":1,"163":1,"207":1,"245":1,"272":1,"274":1},"branchMap":{},"b":{},"fnMap":{"0":{"name":"validateReview","decl":{"start":{"line":17,"column":0},"end":{"line":26,"column":1}},"loc":{"start":{"line":17,"column":0},"end":{"line":26,"column":1}},"line":17}},"f":{"0":0}} +,"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/src/routes/user.routes.ts": {"path":"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/src/routes/user.routes.ts","all":false,"statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":75}},"1":{"start":{"line":2,"column":0},"end":{"line":2,"column":49}},"2":{"start":{"line":3,"column":0},"end":{"line":3,"column":8}},"11":{"start":{"line":12,"column":0},"end":{"line":12,"column":59}},"12":{"start":{"line":13,"column":0},"end":{"line":13,"column":24}},"14":{"start":{"line":15,"column":0},"end":{"line":15,"column":44}},"133":{"start":{"line":134,"column":0},"end":{"line":134,"column":39}},"134":{"start":{"line":135,"column":0},"end":{"line":135,"column":72}},"135":{"start":{"line":136,"column":0},"end":{"line":136,"column":51}},"136":{"start":{"line":137,"column":0},"end":{"line":137,"column":3}},"138":{"start":{"line":139,"column":0},"end":{"line":139,"column":32}},"139":{"start":{"line":140,"column":0},"end":{"line":140,"column":15}},"140":{"start":{"line":141,"column":0},"end":{"line":141,"column":16}},"141":{"start":{"line":142,"column":0},"end":{"line":142,"column":21}},"142":{"start":{"line":143,"column":0},"end":{"line":143,"column":3}},"143":{"start":{"line":144,"column":0},"end":{"line":144,"column":58}},"144":{"start":{"line":145,"column":0},"end":{"line":145,"column":24}},"145":{"start":{"line":146,"column":0},"end":{"line":146,"column":80}},"146":{"start":{"line":147,"column":0},"end":{"line":147,"column":3}},"147":{"start":{"line":148,"column":0},"end":{"line":148,"column":9}},"148":{"start":{"line":149,"column":0},"end":{"line":149,"column":1}},"176":{"start":{"line":177,"column":0},"end":{"line":177,"column":75}},"179":{"start":{"line":180,"column":0},"end":{"line":180,"column":26}},"208":{"start":{"line":209,"column":0},"end":{"line":209,"column":36}},"225":{"start":{"line":226,"column":0},"end":{"line":226,"column":40}},"273":{"start":{"line":274,"column":0},"end":{"line":274,"column":73}},"306":{"start":{"line":307,"column":0},"end":{"line":307,"column":58}},"338":{"start":{"line":339,"column":0},"end":{"line":339,"column":71}},"360":{"start":{"line":361,"column":0},"end":{"line":361,"column":42}},"362":{"start":{"line":363,"column":0},"end":{"line":363,"column":26}}},"s":{"0":1,"1":1,"2":1,"11":1,"12":1,"14":1,"133":1,"134":1,"135":1,"136":1,"138":0,"139":0,"140":0,"141":0,"142":0,"143":0,"144":0,"145":0,"146":0,"147":0,"148":0,"176":1,"179":1,"208":1,"225":1,"273":1,"306":1,"338":1,"360":1,"362":1},"branchMap":{},"b":{},"fnMap":{"0":{"name":"validateUpdatePassword","decl":{"start":{"line":139,"column":0},"end":{"line":149,"column":1}},"loc":{"start":{"line":139,"column":0},"end":{"line":149,"column":1}},"line":139}},"f":{"0":0}} +,"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/src/utils/runMigrations.ts": {"path":"/Users/macbookpro/Documents/Projects_2025/farming-products/farming-product-REST-api/src/utils/runMigrations.ts","all":true,"statementMap":{"1":{"start":{"line":2,"column":0},"end":{"line":2,"column":48}},"8":{"start":{"line":9,"column":0},"end":{"line":9,"column":70}},"9":{"start":{"line":10,"column":0},"end":{"line":10,"column":27}},"10":{"start":{"line":11,"column":0},"end":{"line":11,"column":17}},"11":{"start":{"line":12,"column":0},"end":{"line":12,"column":56}},"12":{"start":{"line":13,"column":0},"end":{"line":13,"column":6}},"13":{"start":{"line":14,"column":0},"end":{"line":14,"column":43}},"14":{"start":{"line":15,"column":0},"end":{"line":15,"column":49}},"15":{"start":{"line":16,"column":0},"end":{"line":16,"column":20}},"16":{"start":{"line":17,"column":0},"end":{"line":17,"column":5}},"18":{"start":{"line":19,"column":0},"end":{"line":19,"column":7}},"19":{"start":{"line":20,"column":0},"end":{"line":20,"column":21}},"20":{"start":{"line":21,"column":0},"end":{"line":21,"column":69}},"21":{"start":{"line":22,"column":0},"end":{"line":22,"column":19}},"22":{"start":{"line":23,"column":0},"end":{"line":23,"column":56}},"23":{"start":{"line":24,"column":0},"end":{"line":24,"column":16}},"24":{"start":{"line":25,"column":0},"end":{"line":25,"column":3}},"25":{"start":{"line":26,"column":0},"end":{"line":26,"column":2}},"27":{"start":{"line":28,"column":0},"end":{"line":28,"column":29}}},"s":{"1":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"27":0},"branchMap":{"0":{"type":"branch","line":1,"loc":{"start":{"line":1,"column":736},"end":{"line":28,"column":29}},"locations":[{"start":{"line":1,"column":736},"end":{"line":28,"column":29}}]}},"b":{"0":[1]},"fnMap":{"0":{"name":"(empty-report)","decl":{"start":{"line":1,"column":736},"end":{"line":28,"column":29}},"loc":{"start":{"line":1,"column":736},"end":{"line":28,"column":29}},"line":1}},"f":{"0":1}} +} diff --git a/coverage/farming-product-REST-api/app.ts.html b/coverage/farming-product-REST-api/app.ts.html new file mode 100644 index 0000000..ad71e48 --- /dev/null +++ b/coverage/farming-product-REST-api/app.ts.html @@ -0,0 +1,562 @@ + + + + + + Code coverage report for farming-product-REST-api/app.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api app.ts

+
+ +
+ 88.07% + Statements + 96/109 +
+ + +
+ 25% + Branches + 1/4 +
+ + +
+ 100% + Functions + 0/0 +
+ + +
+ 88.07% + Lines + 96/109 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160  +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +  +  +1x +1x +  +1x +  +1x +  +1x +  +  +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +  +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +  +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +  +  +1x +  +  +1x +  +  +1x +  +  +  +  +1x +1x +1x +  +1x +1x +1x +  +  +1x +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +1x +1x +1x +  +  +1x +  +  +1x +  +  +  +  +  +  +  +  +  +  +1x + 
// app.ts
+import dotenv from "dotenv";
+import express, { Application, Request, Response, NextFunction } from "express";
+import bodyParser from "body-parser";
+import limiter from "./src/middleware/rateLimiter";
+import appRouter from "./src/routes";
+import cors from "cors";
+import path from "path";
+import fs from "fs";
+import errorHandler from "./src/middleware/errorHandler";
+import swaggerUi from "swagger-ui-express";
+import swaggerJsDoc from "swagger-jsdoc";
+import helmet from "helmet";
+import AppError from "./src/errors/customErrors";
+ 
+// Define the server configuration
+const port: number = parseInt(process.env.PORT || "3000", 10);
+const hostname: string = process.env.DB_HOST || "localhost";
+ 
+const isDev = process.env.NODE_ENV !== "production";
+ 
+dotenv.config();
+ 
+const app: Application = express();
+ 
+// Swagger options
+const doc: any = {
+  definition: {
+    openapi: "3.0.0",
+    info: {
+      title: "Farming products API",
+      version: "1.0.0",
+      description: "API Documentation for Farming products",
+      contact: {
+        name: "Farming products",
+      }
+    },
+    servers: [
+      {
+        url: "http://localhost:3000/api/v1",
+        description: "Development on Local server",
+      }
+    ],
+    components: {
+      securitySchemes: {
+        bearerAuth: {
+          type: "http",
+          scheme: "bearer",
+          bearerFormat: "JWT",
+        },
+      },
+      schemas: {
+        // Reference your main models here for Swagger UI
+        Product: {
+          type: "object",
+          required: ["productName", "productCat", "priceType", "price"],
+          properties: {
+            productName: { type: "string" },
+            productCat: { type: "string" },
+            priceType: { type: "string" },
+            price: { type: "number" },
+            description: { type: "string" },
+            wholeSale: { type: "boolean" },
+            imageUrl: { type: "string" },
+            userId: { type: "string" },
+          },
+          example: {
+            productName: "Tomato",
+            productCat: "Vegetable",
+            priceType: "per kg",
+            price: 1000,
+            description: "Fresh tomatoes",
+            wholeSale: false,
+            imageUrl: "https://cloudinary.com/image.jpg",
+            userId: "uuid-1234"
+          }
+        },
+        // Add more models as needed
+      },
+    },
+    security: [
+      {
+        bearerAuth: [],
+      },
+    ],
+  },
+  apis: isDev ? ["./src/routes/*.ts"] : ["./dist/routes/*.js"]
+};
+ 
+// Update this to '.ts' as files are converted to TypeScript
+const swaggerSpec = swaggerJsDoc(doc);
+// app.use("/api-docs", swaggerUi.serve, swaggerUi.setup(swaggerJsDoc(doc)));
+ 
+const imagesDir = path.join(__dirname, "/src/public/images");
+ 
+// Ensure the directory exists at app start
+if (!fs.existsSync(imagesDir)) {
+  fs.mkdirSync(imagesDir, { recursive: true });
+}
+ 
+// Middleware setup
+app.use(limiter);
+app.set("trust proxy", 1);
+app.use(helmet());
+ 
+app.use(bodyParser.json());
+app.use(cors());
+app.use("/public/images", express.static("public/images"));
+ 
+// Routes setup
+app.use("/api/v1", appRouter);
+ 
+// @ts-ignore-next-line
+app.use("/api-docs", swaggerUi.serve, swaggerUi.setup(swaggerSpec));
+ 
+// Swagger default route definition
+/**
+ * @swagger
+ * /:
+ *   get:
+ *     summary: Welcome message
+ *     description: Returns a greeting message
+ *     responses:
+ *       200:
+ *         description: A simple greeting message.
+ *         content:
+ *           application/json:
+ *             schema:
+ *               type: object
+ *               properties:
+ *                 message:
+ *                   type: string
+ *                   example: "Hello World!! Farming products_2"
+ */
+app.get("/", (req: Request, res: Response) => {
+  res.send("Hello World!! Farming products_2");
+});
+ 
+// 404 handler
+app.use((req: Request, res: Response, next: NextFunction) => {
+  next(new AppError(`Not Found - ${req.originalUrl}`, 404));
+});
+ 
+// Error handling middleware - must be last
+app.use(errorHandler);
+ 
+// Only start the server if we're not in a test environment
+if (process.env.NODE_ENV !== 'test') {
+  app.listen(port, async () => {
+    try {
+      console.log("✅ Database is up to date.");
+    } catch (error) {
+      console.error("❌ Unable to connect to the database:", error);
+    }
+    console.log(`Server running on http://localhost:${port}`);
+  });
+}
+ 
+export default app;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/farming-product-REST-api/index.html b/coverage/farming-product-REST-api/index.html new file mode 100644 index 0000000..5320eb3 --- /dev/null +++ b/coverage/farming-product-REST-api/index.html @@ -0,0 +1,116 @@ + + + + + + Code coverage report for farming-product-REST-api + + + + + + + + + +
+
+

All files farming-product-REST-api

+
+ +
+ 88.07% + Statements + 96/109 +
+ + +
+ 25% + Branches + 1/4 +
+ + +
+ 100% + Functions + 0/0 +
+ + +
+ 88.07% + Lines + 96/109 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
app.ts +
+
88.07%96/10925%1/4100%0/088.07%96/109
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/farming-product-REST-api/src/config/config.ts.html b/coverage/farming-product-REST-api/src/config/config.ts.html new file mode 100644 index 0000000..6bb0fa1 --- /dev/null +++ b/coverage/farming-product-REST-api/src/config/config.ts.html @@ -0,0 +1,166 @@ + + + + + + Code coverage report for farming-product-REST-api/src/config/config.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api/src/config config.ts

+
+ +
+ 0% + Statements + 0/25 +
+ + +
+ 0% + Branches + 0/1 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 0% + Lines + 0/25 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import dotenv from "dotenv";
+ 
+dotenv.config();
+ 
+export default {
+  development: {
+    username: process.env.DB_USERNAME,
+    password: process.env.DB_PASSWORD,
+    database: process.env.DB_NAME,
+    host: process.env.DB_HOST,
+    dialect: "postgres",
+  },
+  test: {
+    username: process.env.DB_USERNAME,
+    password: process.env.DB_PASSWORD,
+    database: process.env.DB_NAME,
+    host: process.env.DB_HOST,
+    dialect: "postgres",
+  },
+  production: {
+    username: process.env.DB_USERNAME,
+    password: process.env.DB_PASSWORD,
+    database: process.env.DB_NAME,
+    host: process.env.DB_HOST,
+    dialect: "postgres",
+  },
+};
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/farming-product-REST-api/src/config/index.html b/coverage/farming-product-REST-api/src/config/index.html new file mode 100644 index 0000000..daa8fc3 --- /dev/null +++ b/coverage/farming-product-REST-api/src/config/index.html @@ -0,0 +1,116 @@ + + + + + + Code coverage report for farming-product-REST-api/src/config + + + + + + + + + +
+
+

All files farming-product-REST-api/src/config

+
+ +
+ 0% + Statements + 0/25 +
+ + +
+ 0% + Branches + 0/1 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 0% + Lines + 0/25 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
config.ts +
+
0%0/250%0/10%0/10%0/25
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/farming-product-REST-api/src/controllers/auth.controller.ts.html b/coverage/farming-product-REST-api/src/controllers/auth.controller.ts.html new file mode 100644 index 0000000..e798709 --- /dev/null +++ b/coverage/farming-product-REST-api/src/controllers/auth.controller.ts.html @@ -0,0 +1,2131 @@ + + + + + + Code coverage report for farming-product-REST-api/src/controllers/auth.controller.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api/src/controllers auth.controller.ts

+
+ +
+ 18.21% + Statements + 94/516 +
+ + +
+ 50% + Branches + 9/18 +
+ + +
+ 22.22% + Functions + 2/9 +
+ + +
+ 18.21% + Lines + 94/516 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +335 +336 +337 +338 +339 +340 +341 +342 +343 +344 +345 +346 +347 +348 +349 +350 +351 +352 +353 +354 +355 +356 +357 +358 +359 +360 +361 +362 +363 +364 +365 +366 +367 +368 +369 +370 +371 +372 +373 +374 +375 +376 +377 +378 +379 +380 +381 +382 +383 +384 +385 +386 +387 +388 +389 +390 +391 +392 +393 +394 +395 +396 +397 +398 +399 +400 +401 +402 +403 +404 +405 +406 +407 +408 +409 +410 +411 +412 +413 +414 +415 +416 +417 +418 +419 +420 +421 +422 +423 +424 +425 +426 +427 +428 +429 +430 +431 +432 +433 +434 +435 +436 +437 +438 +439 +440 +441 +442 +443 +444 +445 +446 +447 +448 +449 +450 +451 +452 +453 +454 +455 +456 +457 +458 +459 +460 +461 +462 +463 +464 +465 +466 +467 +468 +469 +470 +471 +472 +473 +474 +475 +476 +477 +478 +479 +480 +481 +482 +483 +484 +485 +486 +487 +488 +489 +490 +491 +492 +493 +494 +495 +496 +497 +498 +499 +500 +501 +502 +503 +504 +505 +506 +507 +508 +509 +510 +511 +512 +513 +514 +515 +516 +517 +518 +519 +520 +521 +522 +523 +524 +525 +526 +527 +528 +529 +530 +531 +532 +533 +534 +535 +536 +537 +538 +539 +540 +541 +542 +543 +544 +545 +546 +547 +548 +549 +550 +551 +552 +553 +554 +555 +556 +557 +558 +559 +560 +561 +562 +563 +564 +565 +566 +567 +568 +569 +570 +571 +572 +573 +574 +575 +576 +577 +578 +579 +580 +581 +582 +583 +584 +585 +586 +587 +588 +589 +590 +591 +592 +593 +594 +595 +596 +597 +598 +599 +600 +601 +602 +603 +604 +605 +606 +607 +608 +609 +610 +611 +612 +613 +614 +615 +616 +617 +618 +619 +620 +621 +622 +623 +624 +625 +626 +627 +628 +629 +630 +631 +632 +633 +634 +635 +636 +637 +638 +639 +640 +641 +642 +643 +644 +645 +646 +647 +648 +649 +650 +651 +652 +653 +654 +655 +656 +657 +658 +659 +660 +661 +662 +663 +664 +665 +666 +667 +668 +669 +670 +671 +672 +673 +674 +675 +676 +677 +678 +679 +680 +681 +682 +6831x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +  +1x +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +1x +1x +1x +1x +  +1x +  +  +1x +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +2x +2x +2x +2x +2x +2x +  +  +2x +1x +1x +  +2x +  +  +  +2x +  +  +  +1x +1x +1x +1x +2x +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +2x +2x +2x +2x +2x +2x +2x +2x +2x +2x +2x +2x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +2x +2x +2x +2x +2x +2x +2x +2x +2x +  +  +2x +2x +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +1x +1x +1x +1x +1x +1x +  +1x +1x +1x +1x +1x +1x +1x +1x +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +1x +1x +1x +1x +1x +1x +1x +1x +  +  +1x +1x +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import User from "../models/user";
+import Role from "../models/role";
+import UserOTPCode from "../models/userotpcode";
+import { hashSync, compare } from "bcryptjs";
+import { randomInt } from "crypto";
+import jwt from "jsonwebtoken";
+import nodemailer from "nodemailer";
+import { v2 as cloudinary } from "cloudinary";
+import { v4 as uuidv4 } from "uuid";
+import { OAuth2Client } from "google-auth-library";
+import axios from "axios";
+import { NextFunction, Request, Response } from "express";
+import { Readable } from "stream";
+import AppError from "../errors/customErrors";
+ 
+// Type definitions
+interface LoginRequest {
+  email: string;
+  password: string;
+}
+ 
+interface VerifyPhoneRequest {
+  phoneNum: string;
+  password: string;
+  country: string;
+  email: string;
+  userRole: string;
+}
+ 
+interface RegisterUserRequest {
+  firstName: string;
+  lastName: string;
+  address: string;
+  expoPushToken?: string;
+}
+ 
+interface ShippingAddress {
+  id: string;
+  title: string;
+  address: string;
+  default: boolean;
+}
+ 
+interface VerifyOtpRequest {
+  email: string;
+  otp: string;
+}
+ 
+interface AuthHandlerRequest {
+  googleToken?: string;
+  facebookToken?: string;
+}
+ 
+interface SendOtpRequest {
+  email: string;
+}
+ 
+interface GooglePayload {
+  sub: string;
+  email?: string;
+  given_name?: string;
+  family_name?: string;
+  picture?: string;
+}
+ 
+// Configure cloudinary
+cloudinary.config({
+  cloud_name: process.env.CLOUDINARY_CLOUD_NAME,
+  api_key: process.env.CLOUDINARY_API_KEY,
+  api_secret: process.env.CLOUDINARY_API_SECRET,
+});
+ 
+const googleClient = new OAuth2Client(process.env.GMAIL_AUTH_CLIENTID);
+ 
+// Utility function for generating OTP
+const generateOTP = () => randomInt(1000, 9999).toString();
+ 
+// Utility function for sending OTP
+const sendOTP = async (email: string, otp: string, phone: string) => {
+  if (email) {
+    const transporter = nodemailer.createTransport({
+      service: process.env.FARMING_PRODUCTS_PROVIDER,
+      host: process.env.FARMING_PRODUCTS_HOST,
+      port: 587,
+      secure: false,
+      auth: {
+        user: process.env.GMAIL_USER,
+        pass: process.env.GMAIL_APP_PASSWORD,
+      },
+    });
+ 
+    const mailOptions = {
+      from: `Farming products <farmingproductscmr@gmail.com>`,
+      to: email,
+      subject: "Verify Your Email",
+      html: `
+        <h1>Farming products</h1>
+        <p>Hello ${email},</p>
+        <p>Enter <b>${otp}</b> in the app to verify your email address and complete verification:</p>
+        <p>This code <b>expires in 10 minutes</b>.</p>
+        <p>If you did not request this, please ignore this email.</p>
+        <p>Thanks,<br>Farming products Team</p>
+      `,
+    };
+ 
+    await transporter.sendMail(mailOptions);
+  }
+ 
+  if (phone) {
+    const data = {
+      user: process.env.FARMING_PRODUCTS_SMS_USER,
+      password: process.env.FARMING_PRODUCTS_SMS_PASSWORD,
+      senderid: "Farming products",
+      sms: `Farming products. Your OTP is ${otp}, valid for 10 minutes only.`,
+      mobiles: phone,
+    };
+ 
+    const config = {
+      method: "post",
+      url: "https://smsvas.com/bulk/public/index.php/api/v1/sendsms",
+      headers: {
+        "Content-Type": "application/json",
+      },
+      data: JSON.stringify(data),
+    };
+ 
+    await axios.request(config);
+  }
+};
+ 
+// Verify Phone and Handle Registration
+export const verifyPhone = async (
+  req: Request<unknown, unknown, VerifyPhoneRequest>,
+  res: Response,
+  next: NextFunction,
+) => {
+  try {
+    const { phoneNum, password, country, email, userRole } = req.body;
+ 
+    // Input validation
+    if (!email || !password) {
+      throw new AppError("Empty input fields", 400);
+    }
+ 
+    if (password.length < 8) {
+      throw new AppError("Password must be at least 8 characters", 400);
+    }
+ 
+    if (!/^[\w-]+@([\w-]+\.)+[\w-]{2,4}$/.test(email)) {
+      throw new AppError("Invalid email entered", 400);
+    }
+ 
+    const validRoles = ["buyer", "farmer"] as const;
+    if (
+      !userRole ||
+      !validRoles.includes(userRole as (typeof validRoles)[number])
+    ) {
+      throw new AppError(
+        `Invalid role: ${userRole}. Valid roles are: ${validRoles.join(", ")}`,
+        400,
+      );
+    }
+ 
+    // Check if user already exists
+    const userExists = await User.findOne({ where: { email } });
+    if (userExists) {
+      throw new AppError("This email is already registered.", 400);
+    }
+ 
+    // Find or create role
+    let role = await Role.findOne({ where: { roleName: userRole } });
+    if (!role) {
+      role = await Role.create({ roleName: userRole });
+    }
+ 
+    // Hash password
+    const hashedPassword = hashSync(password.trim(), 10);
+ 
+    // Create user
+    const user = await User.create({
+      roleId: role.id,
+      email,
+      password: hashedPassword,
+      country,
+      phoneNum: parseInt(phoneNum, 10) || 0,
+      firstName: "",
+      lastName: "",
+      imageUrl: "",
+      address: "",
+      googleId: "",
+      verifiedUser: false,
+      vip: false,
+      facebookId: "",
+      shipAddress: [],
+      createdAt: new Date(),
+      updatedAt: new Date(),
+    });
+ 
+    if (!user) {
+      throw new AppError("Failed to create user during registration", 500);
+    }
+ 
+    // Generate and store OTP
+    const otp = generateOTP();
+    const hashedOtp = hashSync(otp, 10);
+ 
+    // Create OTP object
+    const otpObj = await UserOTPCode.create({
+      userId: user.id,
+      otp: hashedOtp,
+      expiredAt: new Date(Date.now() + 10 * 60 * 1000), // 10 minutes expiry
+      createdAt: new Date(),
+      updatedAt: new Date(),
+    });
+ 
+    if (!otpObj) {
+      throw new AppError("Failed to create OTP in the database", 500);
+    }
+ 
+    // Send OTP via email and/or SMS
+    await sendOTP(email, otp, phoneNum);
+ 
+    // Return success response
+    return res.status(200).json({
+      message: "OTP sent successfully, please verify the OTP",
+      email: user.email,
+      phoneNum: user.phoneNum,
+      userID: user.id,
+    });
+  } catch (error) {
+    if (error instanceof Error) {
+      next(
+        new AppError(
+          error.message,
+          error instanceof AppError ? error.statusCode : 500,
+        ),
+      );
+    } else {
+      next(new AppError("An unexpected error occurred", 500));
+    }
+  }
+};
+ 
+// Register User
+export const register_user = async (
+  req: Request<{ userId: string }, unknown, RegisterUserRequest>,
+  res: Response,
+  next: NextFunction,
+) => {
+  try {
+    req.file = {
+      path: "https://randomuser.me/api/portraits/men/1.jpg",
+      originalname: "profile.jpg",
+      mimetype: "image/jpeg",
+      size: 12345,
+      fieldname: "image",
+      encoding: "7bit",
+      stream: new Readable(),
+      destination: "",
+      buffer: Buffer.from([]),
+      filename: "profile.jpg",
+    };
+ 
+    const { userId } = req.params;
+    const { address, expoPushToken } = req.body;
+    const firstName = req.body.firstName.trim();
+    const lastName = req.body.lastName.trim();
+ 
+    const shippAddress: ShippingAddress[] = [
+      {
+        id: uuidv4(),
+        title: "Home",
+        address,
+        default: true,
+      },
+    ];
+ 
+    let imageUrl = "";
+    if (req.file) {
+      const cloudinaryImageUpload = await cloudinary.uploader.upload(
+        req.file.path,
+      );
+      imageUrl = cloudinaryImageUpload.secure_url;
+    }
+ 
+    await User.update(
+      {
+        firstName,
+        lastName,
+        address,
+        imageUrl,
+        shipAddress: shippAddress,
+        expoPushToken,
+        updatedAt: new Date(),
+      },
+      { where: { id: userId } },
+    );
+ 
+    return res.status(200).json({ message: "User successfully registered" });
+  } catch (error) {
+    next(error);
+  }
+};
+ 
+// Verify OTP
+export const verifyOtp = async (
+  req: Request<unknown, unknown, VerifyOtpRequest>,
+  res: Response,
+) => {
+  const { email, otp } = req.body;
+ 
+  try {
+    const currentUser = await User.findOne({ where: { email } });
+    if (!currentUser) {
+      return res
+        .status(401)
+        .json({ message: "The User does not exist. Please sign up!" });
+    }
+ 
+    const getOtp = await UserOTPCode.findOne({
+      where: { userId: currentUser.id },
+    });
+    if (!getOtp) {
+      return res.status(404).json({
+        message: "Server Error! Please Try Again Later by signing up.",
+      });
+    }
+ 
+    const { expiredAt } = getOtp;
+    if (expiredAt < Date.now()) {
+      await UserOTPCode.destroy({ where: { userId: currentUser.id } });
+      return res
+        .status(404)
+        .json({ message: "Code has expired. Please request again" });
+    }
+ 
+    const validOtp = await compare(otp, getOtp.otp);
+    if (validOtp) {
+      await UserOTPCode.destroy({ where: { userId: currentUser.id } });
+      await User.update({ verifiedUser: true }, { where: { email } });
+ 
+      currentUser.password = null;
+      return res
+        .status(200)
+        .json({ message: "Successfully verified!", userId: currentUser.id });
+    } else {
+      return res.status(403).json({ message: "Wrong OTP" });
+    }
+  } catch (error) {
+    if (error instanceof Error) {
+      return res.status(500).json({
+        message: "Internal Server Error!",
+        error: error.message,
+      });
+    }
+    return res.status(500).json({
+      message: "Internal Server Error!",
+    });
+  }
+};
+ 
+// Login
+export const logIn = async (
+  req: Request<unknown, unknown, LoginRequest>,
+  res: Response,
+  next: NextFunction,
+) => {
+  try {
+    const { email, password } = req.body;
+ 
+    const user = await User.findOne({
+      where: { email },
+      include: [
+        {
+          model: Role,
+          attributes: ["roleName"],
+        },
+      ],
+    });
+ 
+    if (!user) {
+      throw new AppError("No user exists for this email address", 403);
+    }
+ 
+    const verifyPassword = await compare(password, user.password as string);
+    if (!verifyPassword) {
+      throw new AppError("Incorrect Password", 403);
+    }
+ 
+    const token = jwt.sign(
+      {
+        UserId: user.id,
+        email: user.email,
+      },
+      process.env.JWT_SECRET as string,
+      { expiresIn: "1h" },
+    );
+ 
+    const refreshToken = jwt.sign(
+      {
+        UserId: user.id,
+        email: user.email,
+      },
+      process.env.JWT_SECRET_REFRESH as string,
+      { expiresIn: "7d" },
+    );
+ 
+    user.password = null;
+ 
+    res.status(200).json({
+      message: "Authentication Successful",
+      token,
+      refreshToken,
+      userData: user,
+    });
+  } catch (error) {
+    if (error instanceof Error) {
+      next(
+        new AppError(
+          error.message,
+          error instanceof AppError ? error.statusCode : 500,
+        ),
+      );
+    } else {
+      next(new AppError("An unexpected error occurred", 500));
+    }
+  }
+};
+ 
+// Refresh Token
+export const refreshToken = async (
+  req: Request,
+  res: Response,
+  next: NextFunction,
+) => {
+  try {
+    const authHeader = req.headers.authorization;
+ 
+    if (!authHeader || !authHeader.startsWith("Bearer ")) {
+      return res.status(401).json({
+        message: "You are either not logged in or your session has expired",
+      });
+    }
+ 
+    // Extract the token
+    const token = authHeader.split(" ")[1];
+ 
+    // Verify and decode the token
+    const decodedToken = jwt.verify(
+      token,
+      process.env.JWT_SECRET_REFRESH as string,
+    ) as jwt.JwtPayload;
+ 
+    if (!decodedToken.exp) {
+      return res.status(403).json({
+        auth: false,
+        message: "Token does not have an expiration time",
+      });
+    }
+ 
+    // Generate a new access token
+    const accessToken = jwt.sign(
+      { UserId: decodedToken.UserId, email: decodedToken.email },
+      process.env.JWT_SECRET as string,
+      { expiresIn: "1h" },
+    );
+ 
+    return res
+      .status(200)
+      .json({ message: "Token refreshed successfully", token: accessToken });
+  } catch (error) {
+    if (error instanceof jwt.JsonWebTokenError) {
+      return res
+        .status(401)
+        .json({ message: "Invalid token, please login again" });
+    }
+    next(error);
+  }
+};
+ 
+// Auth Handler (Google and Facebook Auth)
+export const authHandler = async (
+  req: Request<unknown, unknown, AuthHandlerRequest>,
+  res: Response,
+  next: NextFunction,
+) => {
+  const { googleToken, facebookToken } = req.body;
+ 
+  try {
+    if (!googleToken && !facebookToken) {
+      return res.status(400).json({ message: "Missing authentication token" });
+    }
+ 
+    // Ensure the 'buyer' role exists
+    let role = await Role.findOne({ where: { roleName: "buyer" } });
+    if (!role) {
+      role = await Role.create({ roleName: "buyer" });
+    }
+ 
+    let user;
+ 
+    // Google Authentication
+    if (googleToken) {
+      const ticket = await googleClient.verifyIdToken({
+        idToken: googleToken,
+        audience: process.env.GMAIL_AUTH_CLIENTID,
+      });
+ 
+      const payload = ticket.getPayload() as GooglePayload | undefined;
+      if (!payload) {
+        return res.status(401).json({ message: "Invalid Google token" });
+      }
+ 
+      const {
+        sub: googleId,
+        email = "",
+        given_name: firstName = "",
+        family_name: lastName = "",
+        picture: imageUrl = "",
+      } = payload;
+ 
+      user = await User.findOne({ where: { email } });
+ 
+      if (user) {
+        if (!user.googleId) {
+          user.googleId = googleId;
+          await user.save();
+        } else if (user.googleId !== googleId) {
+          return res
+            .status(400)
+            .json({ message: "Please use your exact email ID to sign in" });
+        }
+      } else {
+        user = await User.create({
+          googleId,
+          email,
+          firstName,
+          lastName,
+          imageUrl,
+          roleId: role.id,
+          shipAddress: [],
+          address: "",
+          phoneNum: 0,
+          country: "",
+          vip: false,
+          verifiedUser: false,
+          facebookId: "",
+          createdAt: new Date(),
+          updatedAt: new Date(),
+        });
+      }
+    }
+ 
+    // Facebook Authentication
+    else if (facebookToken) {
+      const response = await axios.get<{
+        id: string;
+        name: string;
+        email: string;
+        picture?: { data: { url: string } };
+      }>(
+        `https://graph.facebook.com/me?access_token=${facebookToken}&fields=id,name,email,picture`,
+      );
+ 
+      const { id: facebookId, name, email } = response.data;
+ 
+      if (!email) {
+        return res
+          .status(400)
+          .json({ message: "Facebook account must have an email" });
+      }
+ 
+      const [firstName] = name.split(" ");
+      const lastName = name.split(" ")[1] || "";
+ 
+      user = await User.findOne({ where: { email } });
+ 
+      if (user) {
+        if (!user.facebookId) {
+          user.facebookId = facebookId;
+          await user.save();
+        } else if (user.facebookId !== facebookId) {
+          return res
+            .status(400)
+            .json({ message: "Please use your exact email ID to sign in" });
+        }
+      } else {
+        user = await User.create({
+          facebookId,
+          email,
+          firstName,
+          lastName,
+          roleId: role.id,
+          country: "",
+          address: "",
+          shipAddress: [],
+          googleId: "",
+          imageUrl: response.data.picture?.data.url || "",
+          vip: false,
+          verifiedUser: false,
+          phoneNum: 0,
+          createdAt: new Date(),
+          updatedAt: new Date(),
+        });
+      }
+    }
+ 
+    if (!user) {
+      return res
+        .status(500)
+        .json({ message: "User creation failed. Please try again." });
+    }
+ 
+    // Generate JWT Token
+    const token = jwt.sign(
+      { UserId: user.id, email: user.email, roleId: user.roleId },
+      process.env.JWT_SECRET as string,
+      { expiresIn: "1h" },
+    );
+ 
+    return res.status(200).json({
+      message: "User logged in successfully",
+      token,
+      user,
+    });
+  } catch (error) {
+    next(error);
+  }
+};
+ 
+// send new OTP
+export const sendNewOTP = async (
+  req: Request<unknown, unknown, SendOtpRequest>,
+  res: Response,
+  next: NextFunction,
+) => {
+  const { email } = req.body;
+  if (!email) {
+    return res.status(400).json({ message: "Email is required" });
+  }
+ 
+  try {
+    const user = await User.findOne({ where: { email } });
+    if (!user) {
+      return res.status(400).json({
+        message:
+          "This email is not registered. Please enter the email with which you have registered your account!",
+      });
+    }
+ 
+    const code = generateOTP();
+ 
+    // Send OTP message
+    await sendOTP(email, code, user.phoneNum?.toString() || "");
+ 
+    // Save OTP to the database
+    const hashedOtp = hashSync(code, 10);
+    const otpObj = await UserOTPCode.create({
+      userId: user.id,
+      otp: hashedOtp,
+      expiredAt: new Date(Date.now() + 10 * 60 * 1000), // 10 minutes expiry
+      createdAt: new Date(),
+      updatedAt: new Date(),
+    });
+ 
+    if (!otpObj) {
+      throw new AppError("Failed to create OTP in the database", 500);
+    }
+ 
+    // Return result
+    return res.status(200).json({
+      message: "OTP sent successfully, please verify the OTP",
+      userID: user.id,
+      email: user.email,
+    });
+  } catch (error) {
+    if (error instanceof AppError) {
+      return res.status(error.statusCode).json({ message: error.message });
+    }
+    next(error);
+  }
+};
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/farming-product-REST-api/src/controllers/index.html b/coverage/farming-product-REST-api/src/controllers/index.html new file mode 100644 index 0000000..32d9891 --- /dev/null +++ b/coverage/farming-product-REST-api/src/controllers/index.html @@ -0,0 +1,236 @@ + + + + + + Code coverage report for farming-product-REST-api/src/controllers + + + + + + + + + +
+
+

All files farming-product-REST-api/src/controllers

+
+ +
+ 7.8% + Statements + 177/2269 +
+ + +
+ 45% + Branches + 9/20 +
+ + +
+ 4.87% + Functions + 2/41 +
+ + +
+ 7.8% + Lines + 177/2269 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
auth.controller.ts +
+
18.21%94/51650%9/1822.22%2/918.21%94/516
multerStorage.ts +
+
0%0/560%0/10%0/10%0/56
notification.controller.ts +
+
5.18%7/135100%0/00%0/45.18%7/135
order.controller.ts +
+
5.58%24/430100%0/00%0/75.58%24/430
payment.collection.controller.ts +
+
0%0/3000%0/10%0/10%0/300
product.controller.ts +
+
7.89%18/228100%0/00%0/67.89%18/228
product.search.controller.ts +
+
4.95%6/121100%0/00%0/14.95%6/121
review.controller.ts +
+
4.77%13/272100%0/00%0/54.77%13/272
user.controller.ts +
+
7.1%15/211100%0/00%0/77.1%15/211
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/farming-product-REST-api/src/controllers/multerStorage.ts.html b/coverage/farming-product-REST-api/src/controllers/multerStorage.ts.html new file mode 100644 index 0000000..3a603ff --- /dev/null +++ b/coverage/farming-product-REST-api/src/controllers/multerStorage.ts.html @@ -0,0 +1,310 @@ + + + + + + Code coverage report for farming-product-REST-api/src/controllers/multerStorage.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api/src/controllers multerStorage.ts

+
+ +
+ 0% + Statements + 0/56 +
+ + +
+ 0% + Branches + 0/1 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 0% + Lines + 0/56 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import multer, { StorageEngine, MulterError, Multer } from "multer";
+import path from "path";
+import { Request, Response, NextFunction } from "express";
+import AppError from "../errors/customErrors";
+ 
+// Define custom types for multer request and response
+type MulterRequest = Request & { file?: Express.Multer.File };
+type MulterResponse = Response;
+ 
+// Define the storage configuration
+const storage: StorageEngine = multer.diskStorage({
+  destination: (_req, _file, cb) => {
+    cb(null, "./src/public/images");
+  },
+  filename: (_req, file, cb) => {
+    const uniqueSuffix = Date.now() + "-" + Math.round(Math.random() * 1e9);
+    cb(
+      null,
+      file.fieldname + "-" + uniqueSuffix + path.extname(file.originalname),
+    );
+  },
+});
+ 
+// Create multer instance with configuration
+const upload: Multer = multer({
+  storage,
+  limits: {
+    fileSize: 5 * 1024 * 1024, // 5MB max file size
+  },
+  fileFilter: (_req, file, cb) => {
+    // Accept only image files
+    if (!file.mimetype.startsWith("image")) {
+      cb(new Error("Only image files are allowed"));
+      return;
+    }
+    cb(null, true);
+  },
+});
+ 
+// Factory function to create the upload middleware
+const uploadMiddleware = (fieldName: string) => {
+  const middleware = upload.single(fieldName);
+ 
+  return (
+    req: MulterRequest,
+    res: MulterResponse,
+    next: NextFunction,
+  ): void => {
+    // Type assertion is necessary here due to multer's type incompatibility
+    const multerReq = req as unknown as Request;
+    const multerRes = res as unknown as Response;
+    const multerMiddleware = middleware as unknown as (
+      req: Request,
+      res: Response,
+      callback: (err: unknown) => void,
+    ) => void;
+    multerMiddleware(multerReq, multerRes, (err: unknown) => {
+      if (err instanceof MulterError) {
+        if (err.code === "LIMIT_FILE_SIZE") {
+          next(new AppError("File size too large. Maximum size is 5MB", 400));
+          return;
+        }
+        next(new AppError(err.message, 400));
+        return;
+      }
+      if (err instanceof Error) {
+        next(new AppError(err.message, 400));
+        return;
+      }
+      next();
+    });
+  };
+};
+ 
+export default uploadMiddleware;
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/farming-product-REST-api/src/controllers/notification.controller.ts.html b/coverage/farming-product-REST-api/src/controllers/notification.controller.ts.html new file mode 100644 index 0000000..685c276 --- /dev/null +++ b/coverage/farming-product-REST-api/src/controllers/notification.controller.ts.html @@ -0,0 +1,637 @@ + + + + + + Code coverage report for farming-product-REST-api/src/controllers/notification.controller.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api/src/controllers notification.controller.ts

+
+ +
+ 5.18% + Statements + 7/135 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/4 +
+ + +
+ 5.18% + Lines + 7/135 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +1851x +1x +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import Notification from "../models/notifiation";
+import User from "../models/user";
+import { Request, Response, NextFunction } from "express";
+import AppError from "../errors/customErrors";
+ 
+interface ExpoMessage {
+  to: string;
+  sound: "default" | "custom";
+  title: string;
+  body: string;
+  data?: Record<string, unknown>;
+  priority?: "default" | "normal" | "high";
+}
+ 
+interface ExpoResponse {
+  data: {
+    status: "ok" | "error";
+    id: string;
+    message: string;
+  }[];
+}
+ 
+interface CreateNotificationRequest {
+  title: string;
+  message: string;
+}
+ 
+// Get all notifications for a user
+export const getNotification = async (
+  req: Request<{ userId: string }>,
+  res: Response,
+  next: NextFunction,
+): Promise<void> => {
+  const { userId } = req.params;
+ 
+  try {
+    const allNotifications = await Notification.findAndCountAll({
+      where: { userId },
+      order: [["createdAt", "DESC"]],
+      include: {
+        model: User,
+        attributes: ["id", "firstName", "lastName", "country", "verifiedUser"],
+      },
+    });
+ 
+    res.status(200).json({
+      notifications: allNotifications,
+    });
+  } catch (error) {
+    next(error);
+  }
+};
+ 
+// Create a new notification
+export const createNotification = async (
+  req: Request<{ userId: string }, unknown, CreateNotificationRequest>,
+  res: Response,
+  next: NextFunction,
+): Promise<void> => {
+  const { userId } = req.params;
+  const { title, message } = req.body;
+ 
+  try {
+    if (!title || !message) {
+      throw new AppError("Title and message are required", 400);
+    }
+ 
+    const notification = await Notification.create({
+      userId,
+      message,
+      title,
+      isRead: false,
+      createdAt: new Date(),
+      updatedAt: new Date(),
+    });
+ 
+    if (!notification) {
+      throw new AppError("Failed to create notification", 500);
+    }
+ 
+    res.status(200).json({
+      result: notification,
+    });
+  } catch (error) {
+    if (error instanceof AppError) {
+      res.status(error.statusCode).json({ message: error.message });
+      return;
+    }
+    next(error);
+  }
+};
+ 
+// Mark a notification as read
+export const markAsRead = async (
+  req: Request<{ id: string }>,
+  res: Response,
+  next: NextFunction,
+): Promise<void> => {
+  const { id: notificationId } = req.params;
+ 
+  try {
+    const [updatedCount] = await Notification.update(
+      { isRead: true, updatedAt: new Date() },
+      { where: { id: notificationId } },
+    );
+ 
+    if (updatedCount === 0) {
+      throw new AppError("Notification not found", 404);
+    }
+ 
+    res.status(200).json({
+      message: "Notification marked as read",
+    });
+  } catch (error) {
+    if (error instanceof AppError) {
+      res.status(error.statusCode).json({ message: error.message });
+      return;
+    }
+    next(error);
+  }
+};
+ 
+// Test Expo notification
+export const testExpoNotification = async (
+  req: Request<{ userId: string }>,
+  res: Response,
+  next: NextFunction,
+): Promise<void> => {
+  const { userId } = req.params;
+ 
+  try {
+    const userData = await User.findOne({ where: { id: userId } });
+    if (!userData) {
+      throw new AppError("User not found", 404);
+    }
+ 
+    if (!userData.expoPushToken) {
+      throw new AppError("User has no push token registered", 400);
+    }
+ 
+    const message: ExpoMessage = {
+      to: userData.expoPushToken,
+      sound: "default",
+      title: "Test Notification",
+      body: "A test notification",
+      priority: "high",
+    };
+ 
+    const response = await fetch("https://exp.host/--/api/v2/push/send", {
+      method: "POST",
+      headers: {
+        Accept: "application/json",
+        "Content-Type": "application/json",
+      },
+      body: JSON.stringify(message),
+    });
+ 
+    if (!response.ok) {
+      throw new AppError(
+        `Failed to send notification: ${response.statusText}`,
+        response.status,
+      );
+    }
+ 
+    const result = (await response.json()) as ExpoResponse;
+ 
+    // Check for errors in the response
+    const error = result.data?.find((item) => item.status === "error");
+    if (error) {
+      throw new AppError(`Expo notification error: ${error.message}`, 500);
+    }
+ 
+    res.status(200).json({
+      message: "Notification sent successfully",
+      result,
+    });
+  } catch (error) {
+    if (error instanceof AppError) {
+      res.status(error.statusCode).json({ message: error.message });
+      return;
+    }
+    next(error);
+  }
+};
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/farming-product-REST-api/src/controllers/order.controller.ts.html b/coverage/farming-product-REST-api/src/controllers/order.controller.ts.html new file mode 100644 index 0000000..b0244ae --- /dev/null +++ b/coverage/farming-product-REST-api/src/controllers/order.controller.ts.html @@ -0,0 +1,1708 @@ + + + + + + Code coverage report for farming-product-REST-api/src/controllers/order.controller.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api/src/controllers order.controller.ts

+
+ +
+ 5.58% + Statements + 24/430 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/7 +
+ + +
+ 5.58% + Lines + 24/430 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +335 +336 +337 +338 +339 +340 +341 +342 +343 +344 +345 +346 +347 +348 +349 +350 +351 +352 +353 +354 +355 +356 +357 +358 +359 +360 +361 +362 +363 +364 +365 +366 +367 +368 +369 +370 +371 +372 +373 +374 +375 +376 +377 +378 +379 +380 +381 +382 +383 +384 +385 +386 +387 +388 +389 +390 +391 +392 +393 +394 +395 +396 +397 +398 +399 +400 +401 +402 +403 +404 +405 +406 +407 +408 +409 +410 +411 +412 +413 +414 +415 +416 +417 +418 +419 +420 +421 +422 +423 +424 +425 +426 +427 +428 +429 +430 +431 +432 +433 +434 +435 +436 +437 +438 +439 +440 +441 +442 +443 +444 +445 +446 +447 +448 +449 +450 +451 +452 +453 +454 +455 +456 +457 +458 +459 +460 +461 +462 +463 +464 +465 +466 +467 +468 +469 +470 +471 +472 +473 +474 +475 +476 +477 +478 +479 +480 +481 +482 +483 +484 +485 +486 +487 +488 +489 +490 +491 +492 +493 +494 +495 +496 +497 +498 +499 +500 +501 +502 +503 +504 +505 +506 +507 +508 +509 +510 +511 +512 +513 +514 +515 +516 +517 +518 +519 +520 +521 +522 +523 +524 +525 +526 +527 +528 +529 +530 +531 +532 +533 +534 +535 +536 +537 +538 +539 +540 +541 +5421x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +1x +1x +1x +1x +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import User from "../models/user";
+import BuyerReview from "../models/buyerreview";
+import Product from "../models/product";
+import Order from "../models/order";
+import Transaction from "../models/transaction";
+import sequelize from "../models";
+import { Op, WhereOptions } from "sequelize";
+import { v2 as cloudinary } from "cloudinary";
+import * as fs from "fs";
+import sendPushNotificationToUser from "../middleware/send-notification";
+import handleExpoResponse, {
+  ExpoResponse,
+} from "../middleware/handleExpoResponse";
+import { Request, Response, NextFunction } from "express";
+import AppError from "../errors/customErrors";
+ 
+// Type definitions
+interface OrderWhereClause {
+  sellerId?: string;
+  buyerId?: string;
+  status?: string | { [Op.like]: string };
+}
+ 
+interface ProductWhereClause {
+  productName?: { [Op.like]: string };
+}
+ 
+interface PushNotificationMessage {
+  title: string;
+  text: string;
+}
+ 
+interface NotificationMessage {
+  title: string;
+  message: string;
+}
+ 
+interface DispatchDetails {
+  dispatchedAt: Date;
+  method: string;
+  imageUrl?: string;
+}
+ 
+interface CreateOrderRequest {
+  amount: number;
+  shipAddress: string;
+  weight: string;
+  sellerId: string;
+}
+ 
+interface UpdateOrderRequest {
+  userId: string;
+}
+ 
+interface DispatchRequest {
+  method: string;
+  date: Date;
+}
+ 
+// Configure cloudinary
+cloudinary.config({
+  cloud_name: process.env.CLOUDINARY_CLOUD_NAME,
+  api_key: process.env.CLOUDINARY_API_KEY,
+  api_secret: process.env.CLOUDINARY_API_SECRET,
+});
+ 
+// Get order by ID
+export const getOrderById = async (
+  req: Request<{ orderId: string }>,
+  res: Response,
+): Promise<void> => {
+  try {
+    const orderData = await Order.findOne({
+      where: { id: req.params.orderId },
+      include: [
+        {
+          model: User,
+          as: "buyer",
+          attributes: [
+            "id",
+            "firstName",
+            "lastName",
+            "country",
+            "verifiedUser",
+          ],
+        },
+        {
+          model: User,
+          as: "seller",
+          attributes: [
+            "id",
+            "firstName",
+            "lastName",
+            "country",
+            "verifiedUser",
+          ],
+        },
+        {
+          model: Product,
+          include: [
+            {
+              model: User,
+              attributes: [
+                "id",
+                "firstName",
+                "lastName",
+                "country",
+                "verifiedUser",
+              ],
+            },
+            {
+              model: BuyerReview,
+              attributes: ["id", "comment", "rating"],
+              required: false,
+            },
+          ],
+        },
+      ],
+    });
+ 
+    if (!orderData) {
+      throw new AppError("Order not found", 404);
+    }
+ 
+    res.status(200).json({
+      status: "success",
+      order: orderData,
+    });
+  } catch (error) {
+    if (error instanceof AppError) {
+      res.status(error.statusCode).json({ message: error.message });
+      return;
+    }
+    res.status(500).json({
+      message: error instanceof Error ? error.message : "An error occurred",
+    });
+  }
+};
+ 
+// Get buyer orders
+export const getBuyerOrders = async (
+  req: Request<{ buyerId: string }, unknown, unknown, { orderStatus?: string }>,
+  res: Response,
+): Promise<void> => {
+  const { buyerId } = req.params;
+  const { orderStatus } = req.query;
+ 
+  const whereClause: OrderWhereClause = {
+    buyerId,
+  };
+ 
+  if (orderStatus && orderStatus.trim() !== "") {
+    whereClause.status = orderStatus;
+  }
+ 
+  try {
+    const buyerOrders = await Order.findAndCountAll({
+      where: whereClause as WhereOptions,
+      include: [
+        {
+          model: User,
+          as: "seller",
+          attributes: [
+            "id",
+            "firstName",
+            "lastName",
+            "country",
+            "verifiedUser",
+          ],
+        },
+        {
+          model: Product,
+          include: [
+            {
+              model: User,
+              attributes: [
+                "id",
+                "firstName",
+                "lastName",
+                "country",
+                "verifiedUser",
+              ],
+            },
+            {
+              model: BuyerReview,
+              attributes: ["id", "comment", "rating"],
+              required: false,
+            },
+          ],
+        },
+      ],
+    });
+ 
+    res.status(200).json({
+      status: "success",
+      ordersData: buyerOrders,
+    });
+  } catch (error) {
+    res.status(500).json({
+      message: error instanceof Error ? error.message : "An error occurred",
+    });
+  }
+};
+ 
+// Get seller orders
+export const getSellerOrders = async (
+  req: Request<
+    { sellerId: string },
+    unknown,
+    unknown,
+    { orderStatus?: string; productName?: string }
+  >,
+  res: Response,
+): Promise<void> => {
+  const { sellerId } = req.params;
+  const { orderStatus, productName } = req.query;
+ 
+  const whereClause: OrderWhereClause = {
+    sellerId,
+  };
+ 
+  const prodWhereClause: ProductWhereClause = {};
+ 
+  if (orderStatus && orderStatus.trim() !== "") {
+    whereClause.status = orderStatus;
+  }
+  if (productName && productName.trim() !== "") {
+    prodWhereClause.productName = {
+      [Op.like]: `%${productName}%`,
+    };
+  }
+ 
+  try {
+    const sellerOrders = await Order.findAndCountAll({
+      where: whereClause as WhereOptions,
+      include: [
+        {
+          model: User,
+          as: "buyer",
+          attributes: [
+            "id",
+            "firstName",
+            "lastName",
+            "country",
+            "verifiedUser",
+          ],
+        },
+        {
+          model: Product,
+          where: prodWhereClause as WhereOptions,
+          include: [
+            {
+              model: User,
+              attributes: [
+                "id",
+                "firstName",
+                "lastName",
+                "country",
+                "verifiedUser",
+              ],
+            },
+            {
+              model: BuyerReview,
+              attributes: ["id", "comment", "rating"],
+              required: false,
+            },
+          ],
+        },
+      ],
+    });
+ 
+    res.status(200).json({
+      status: "success",
+      ordersData: sellerOrders,
+    });
+  } catch (error) {
+    res.status(500).json({
+      message: error instanceof Error ? error.message : "An error occurred",
+    });
+  }
+};
+ 
+// Create order
+export const createOrder = async (
+  req: Request<{ productId: string }, unknown, CreateOrderRequest>,
+  res: Response,
+): Promise<void> => {
+  const transaction = await sequelize.transaction();
+ 
+  try {
+    const { amount, shipAddress, weight, sellerId } = req.body;
+ 
+    // Validate required fields
+    if (!amount || !shipAddress || !weight || !sellerId) {
+      throw new AppError("Missing required fields", 400);
+    }
+ 
+    // Validate the sellerId Before Inserting
+    const seller = await User.findOne({ where: { id: sellerId } });
+    if (!seller) {
+      throw new AppError("Invalid sellerId: Seller does not exist", 400);
+    }
+ 
+    const order = await Order.create(
+      {
+        amount,
+        shipAddress,
+        weight,
+        sellerId: seller.id,
+        prodId: req.params.productId,
+        buyerId: "3ff1ceec-9f0d-4952-9c6c-fe3973dd8fa1", // TODO: Replace with actual buyer ID from auth
+        status: "pending",
+        dispatched: false,
+        createdAt: new Date(),
+        updatedAt: new Date(),
+      },
+      { transaction },
+    );
+ 
+    await Transaction.create(
+      {
+        amount: order.amount,
+        orderId: order.id,
+        status: "pending",
+        createdAt: new Date(),
+        updatedAt: new Date(),
+      },
+      { transaction },
+    );
+ 
+    await transaction.commit();
+ 
+    res.status(200).json({
+      message:
+        "Order created successfully. Please proceed toward payment else order can not be processed further",
+      orderDetails: order,
+    });
+  } catch (error) {
+    await transaction.rollback();
+    if (error instanceof AppError) {
+      res.status(error.statusCode).json({ message: error.message });
+      return;
+    }
+    res.status(500).json({
+      message: error instanceof Error ? error.message : "An error occurred",
+    });
+  }
+};
+ 
+// Update order
+export const updateOrder = async (
+  req: Request<{ orderId: string }, unknown, UpdateOrderRequest>,
+  res: Response,
+): Promise<void> => {
+  try {
+    const txOrder = await Transaction.findOne({
+      where: { orderId: req.params.orderId },
+    });
+ 
+    if (!txOrder) {
+      throw new AppError("Transaction not found for this order", 404);
+    }
+ 
+    if (txOrder.status !== "completed") {
+      throw new AppError(
+        "This Order is not in Transaction. Please make payment first",
+        403,
+      );
+    }
+ 
+    await Order.update(
+      {
+        status: "processing",
+        updatedAt: new Date(),
+      },
+      {
+        where: { id: req.params.orderId },
+      },
+    );
+ 
+    const orderData = await Order.findByPk(req.params.orderId);
+    if (!orderData) {
+      throw new AppError("Order not found", 404);
+    }
+ 
+    const [sellerData, buyerData] = await Promise.all([
+      User.findByPk(orderData.sellerId),
+      User.findByPk(req.body.userId),
+    ]);
+ 
+    // Send notifications
+    if (sellerData?.expoPushToken) {
+      const notificationMessage: NotificationMessage = {
+        title: "Order Completed",
+        message: "Congratulations! Your Order has been marked as completed",
+      };
+ 
+      const pushMessage: PushNotificationMessage = {
+        title: notificationMessage.title,
+        text: notificationMessage.message,
+      };
+ 
+      const result = await sendPushNotificationToUser(
+        sellerData.expoPushToken,
+        pushMessage,
+      );
+ 
+      if (result && "status" in result) {
+        await handleExpoResponse(
+          result as ExpoResponse,
+          orderData.sellerId,
+          notificationMessage,
+        );
+      }
+    }
+ 
+    if (buyerData?.expoPushToken) {
+      const notificationMessage: NotificationMessage = {
+        title: "Order Completion",
+        message: "You have marked your order as completed",
+      };
+ 
+      const pushMessage: PushNotificationMessage = {
+        title: notificationMessage.title,
+        text: notificationMessage.message,
+      };
+ 
+      const result = await sendPushNotificationToUser(
+        buyerData.expoPushToken,
+        pushMessage,
+      );
+ 
+      if (result && "status" in result) {
+        await handleExpoResponse(
+          result as ExpoResponse,
+          req.body.userId,
+          notificationMessage,
+        );
+      }
+    }
+ 
+    res.status(200).json({ message: "Order Completed Successfully!" });
+  } catch (error) {
+    if (error instanceof AppError) {
+      res.status(error.statusCode).json({ message: error.message });
+      return;
+    }
+    res.status(500).json({
+      message: error instanceof Error ? error.message : "An error occurred",
+    });
+  }
+};
+ 
+// Get transaction
+export const getTransaction = async (
+  req: Request<{ orderId: string }>,
+  res: Response,
+): Promise<void> => {
+  try {
+    const transaction = await Transaction.findOne({
+      where: { orderId: req.params.orderId },
+    });
+ 
+    if (!transaction) {
+      throw new AppError("Transaction not found", 404);
+    }
+ 
+    res.status(200).json({
+      message: "Transaction Details",
+      details: transaction,
+    });
+  } catch (error) {
+    if (error instanceof AppError) {
+      res.status(error.statusCode).json({ message: error.message });
+      return;
+    }
+    res.status(500).json({
+      message: error instanceof Error ? error.message : "An error occurred",
+    });
+  }
+};
+ 
+// Update dispatch details
+export const updateDispatchDetails = async (
+  req: Request<{ orderId: string }, unknown, DispatchRequest>,
+  res: Response,
+  next: NextFunction,
+): Promise<void> => {
+  try {
+    const { orderId } = req.params;
+    const { method, date } = req.body;
+ 
+    if (!method || !date) {
+      throw new AppError("Method and date are required", 400);
+    }
+ 
+    const details: DispatchDetails = {
+      dispatchedAt: new Date(),
+      method,
+    };
+ 
+    if (req.file) {
+      const cloudinaryImageUpload = await cloudinary.uploader.upload(
+        req.file.path,
+        {
+          resource_type: "image",
+        },
+      );
+ 
+      details.imageUrl = cloudinaryImageUpload.secure_url;
+      fs.unlinkSync(req.file.path);
+    }
+ 
+    const [updatedCount] = await Order.update(
+      {
+        status: "dispatched",
+        dispatched: true,
+        dispatchDetails: details,
+        deliveryDate: date,
+        updatedAt: new Date(),
+      },
+      {
+        where: { id: orderId },
+      },
+    );
+ 
+    if (updatedCount === 0) {
+      throw new AppError("Order not found", 404);
+    }
+ 
+    res.status(200).json({
+      message: "Dispatch details updated successfully",
+    });
+  } catch (error) {
+    if (error instanceof AppError) {
+      res.status(error.statusCode).json({ message: error.message });
+      return;
+    }
+    next(error);
+  }
+};
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/farming-product-REST-api/src/controllers/payment.collection.controller.ts.html b/coverage/farming-product-REST-api/src/controllers/payment.collection.controller.ts.html new file mode 100644 index 0000000..d725840 --- /dev/null +++ b/coverage/farming-product-REST-api/src/controllers/payment.collection.controller.ts.html @@ -0,0 +1,1228 @@ + + + + + + Code coverage report for farming-product-REST-api/src/controllers/payment.collection.controller.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api/src/controllers payment.collection.controller.ts

+
+ +
+ 0% + Statements + 0/300 +
+ + +
+ 0% + Branches + 0/1 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 0% + Lines + 0/300 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +335 +336 +337 +338 +339 +340 +341 +342 +343 +344 +345 +346 +347 +348 +349 +350 +351 +352 +353 +354 +355 +356 +357 +358 +359 +360 +361 +362 +363 +364 +365 +366 +367 +368 +369 +370 +371 +372 +373 +374 +375 +376 +377 +378 +379 +380 +381 +382  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import User from "../models/user";
+import Order from "../models/order";
+import Transaction from "../models/transaction";
+import axios, { AxiosError } from "axios";
+import { Request, Response } from "express";
+import sequelize from "../models";
+import sendPushNotificationToUser from "../middleware/send-notification";
+import handleExpoResponse, {
+  ExpoResponse,
+} from "../middleware/handleExpoResponse";
+import AppError from "../errors/customErrors";
+ 
+interface PaymentData {
+  meanCode: string;
+  amount: string;
+  currency: string;
+  orderNumber?: string;
+}
+ 
+interface AdwaResponse {
+  data: {
+    tokenCode?: string;
+    adpFootprint?: string;
+    status?: string;
+    message?: string;
+  };
+}
+ 
+interface PushNotificationMessage {
+  title: string;
+  text: string;
+}
+ 
+interface NotificationMessage {
+  title: string;
+  message: string;
+}
+ 
+interface CollectionWebhookRequest {
+  status: string;
+  footPrint: string;
+  orderNumber: string;
+  moyenPaiement: string;
+  amount: number;
+}
+ 
+// Payment gateway adwapay configuration
+const MERCHANT_KEY = process.env.ADWA_MERCHANT_KEY;
+const APPLICATION_KEY = process.env.ADWA_APPLICATION_KEY;
+const SUBSCRIPTION_KEY = process.env.ADWA_SUBSCRIPTION_KEY;
+const BaseURL_Adwa = process.env.ADWA_BASE_URL;
+ 
+if (!MERCHANT_KEY || !APPLICATION_KEY || !SUBSCRIPTION_KEY || !BaseURL_Adwa) {
+  throw new Error("Missing required Adwa payment configuration");
+}
+ 
+// Get authentication token
+const getAuthToken = async (): Promise<AdwaResponse> => {
+  try {
+    const data = JSON.stringify({
+      application: APPLICATION_KEY,
+    });
+ 
+    const config = {
+      method: "post",
+      url: `${BaseURL_Adwa}/getADPToken`,
+      headers: {
+        "Content-Type": "application/json",
+        Authorization: `Basic ${Buffer.from(MERCHANT_KEY + ":" + SUBSCRIPTION_KEY).toString("base64")}`,
+      },
+      data,
+    };
+ 
+    const response = await axios(config);
+    return response.data;
+  } catch (error) {
+    if (error instanceof AxiosError && error.response) {
+      throw new AppError(
+        `Failed to get auth token: ${error.response.data.message || error.message}`,
+        error.response.status,
+      );
+    }
+    throw new AppError("Failed to get auth token", 500);
+  }
+};
+ 
+// Payment initiation
+const paymentCollectRequest = async (
+  data: PaymentData,
+  token: string,
+): Promise<AdwaResponse> => {
+  try {
+    const config = {
+      method: "post",
+      url: `${BaseURL_Adwa}/requestToPay`,
+      headers: {
+        "AUTH-API-TOKEN": `Bearer ${token}`,
+        "AUTH-API-SUBSCRIPTION": SUBSCRIPTION_KEY,
+        "Content-Type": "application/json",
+      },
+      data: JSON.stringify(data),
+    };
+ 
+    const response = await axios(config);
+    return response.data;
+  } catch (error) {
+    if (error instanceof AxiosError && error.response) {
+      throw new AppError(
+        `Payment request failed: ${error.response.data.message || error.message}`,
+        error.response.status,
+      );
+    }
+    throw new AppError("Payment request failed", 500);
+  }
+};
+ 
+// Check payment status
+const chargeStatusCheck = async (
+  footPrint: string,
+  meanCode: string,
+  token: string,
+): Promise<AdwaResponse> => {
+  try {
+    const data = JSON.stringify({
+      adpFootprint: footPrint,
+      meanCode,
+    });
+ 
+    const config = {
+      method: "post",
+      url: `${BaseURL_Adwa}/paymentStatus`,
+      headers: {
+        "AUTH-API-TOKEN": `Bearer ${token}`,
+        "AUTH-API-SUBSCRIPTION": SUBSCRIPTION_KEY,
+        "Content-Type": "application/json",
+      },
+      data,
+    };
+ 
+    const response = await axios(config);
+    return response.data;
+  } catch (error) {
+    if (error instanceof AxiosError && error.response) {
+      throw new AppError(
+        `Failed to check payment status: ${error.response.data.message || error.message}`,
+        error.response.status,
+      );
+    }
+    throw new AppError("Failed to check payment status", 500);
+  }
+};
+ 
+// Mobile payment collection
+export const mobilePaymentCollection = async (
+  req: Request<{ orderId: string }, unknown, PaymentData>,
+  res: Response,
+): Promise<void> => {
+  const { orderId } = req.params;
+  const paymentData = req.body;
+  const transaction = await sequelize.transaction();
+ 
+  try {
+    const order = await Order.findByPk(orderId);
+    if (!order) {
+      throw new AppError("Order not found or not created", 404);
+    }
+ 
+    const userToken = await getAuthToken();
+    if (!userToken.data.tokenCode) {
+      throw new AppError(
+        "Unable to get the token from the payment service providers. Please try again",
+        403,
+      );
+    }
+ 
+    // Requesting payment initiation
+    paymentData.orderNumber = `order_${orderId}_${Date.now()}`;
+    const paymentRequest = await paymentCollectRequest(
+      paymentData,
+      userToken.data.tokenCode,
+    );
+ 
+    if (
+      paymentData.meanCode === "MASTERCARD" ||
+      paymentData.meanCode === "VISA"
+    ) {
+      res.json({ message: paymentRequest.data });
+      return;
+    }
+ 
+    setTimeout(async () => {
+      try {
+        const resOutput = await chargeStatusCheck(
+          paymentRequest.data.adpFootprint as string,
+          paymentData.meanCode,
+          userToken.data.tokenCode as string,
+        );
+ 
+        if (resOutput.data.status === "T") {
+          await Transaction.update(
+            {
+              amount: parseFloat(paymentData.amount),
+              status: "completed",
+              txMethod: paymentData.meanCode,
+              currency: paymentData.currency,
+              orderId,
+              txDetails: resOutput.data,
+              updatedAt: new Date(),
+            },
+            { where: { orderId }, transaction },
+          );
+ 
+          await transaction.commit();
+ 
+          res.status(200).json({
+            status: "success",
+            message: resOutput.data,
+          });
+        } else {
+          await transaction.rollback();
+          res.status(400).json({
+            response: resOutput,
+            message:
+              "Payment was not successfully processed from the end-user.",
+          });
+        }
+      } catch (error) {
+        await transaction.rollback();
+        if (error instanceof AppError) {
+          res.status(error.statusCode).json({ message: error.message });
+        } else {
+          res.status(500).json({
+            message:
+              error instanceof Error
+                ? error.message
+                : "An error occurred during payment processing",
+          });
+        }
+      }
+    }, 100000);
+  } catch (error) {
+    await transaction.rollback();
+    if (error instanceof AppError) {
+      res.status(error.statusCode).json({ message: error.message });
+    } else {
+      res.status(500).json({
+        message:
+          error instanceof Error
+            ? error.message
+            : "An error occurred during payment processing",
+      });
+    }
+  }
+};
+ 
+// Collection webhook response
+export const collectionResponseAdwa = async (
+  req: Request<unknown, unknown, CollectionWebhookRequest>,
+  res: Response,
+): Promise<void> => {
+  const { status, footPrint, orderNumber, moyenPaiement, amount } = req.body;
+  const transaction = await sequelize.transaction();
+ 
+  try {
+    if (status !== "T") {
+      throw new AppError("Payment validation failed", 400);
+    }
+ 
+    const authToken = await getAuthToken();
+    if (!authToken.data.tokenCode) {
+      throw new AppError("Failed to get authentication token", 500);
+    }
+ 
+    const checkResponse = await chargeStatusCheck(
+      footPrint,
+      moyenPaiement,
+      authToken.data.tokenCode,
+    );
+ 
+    if (checkResponse.data.status !== "T") {
+      throw new AppError("Payment validation failed", 400);
+    }
+ 
+    const order = await Order.findByPk(orderNumber);
+    if (!order) {
+      throw new AppError("Order not found", 404);
+    }
+ 
+    const [sellerData, buyerData] = await Promise.all([
+      User.findByPk(order.sellerId),
+      User.findByPk(order.buyerId),
+    ]);
+ 
+    await Promise.all([
+      Transaction.update(
+        {
+          amount,
+          status: "completed",
+          txMethod: moyenPaiement,
+          txDetails: checkResponse.data,
+          updatedAt: new Date(),
+        },
+        { where: { orderId: order.id }, transaction },
+      ),
+      Order.update(
+        {
+          status: "processing",
+          updatedAt: new Date(),
+        },
+        { where: { id: order.id }, transaction },
+      ),
+    ]);
+ 
+    // Send notifications
+    if (sellerData?.expoPushToken) {
+      const notificationMessage: NotificationMessage = {
+        title: "New Order",
+        message: "Congratulations! You have received a New Order.",
+      };
+ 
+      const pushMessage: PushNotificationMessage = {
+        title: notificationMessage.title,
+        text: notificationMessage.message,
+      };
+ 
+      const result = await sendPushNotificationToUser(
+        sellerData.expoPushToken,
+        pushMessage,
+      );
+ 
+      if (result && "status" in result) {
+        await handleExpoResponse(
+          result as ExpoResponse,
+          order.sellerId,
+          notificationMessage,
+        );
+      }
+    }
+ 
+    if (buyerData?.expoPushToken) {
+      const notificationMessage: NotificationMessage = {
+        title: "Payment Done",
+        message:
+          "Your Payment has been Successfully Made and Your order has started",
+      };
+ 
+      const pushMessage: PushNotificationMessage = {
+        title: notificationMessage.title,
+        text: notificationMessage.message,
+      };
+ 
+      const result = await sendPushNotificationToUser(
+        buyerData.expoPushToken,
+        pushMessage,
+      );
+ 
+      if (result && "status" in result) {
+        await handleExpoResponse(
+          result as ExpoResponse,
+          order.buyerId,
+          notificationMessage,
+        );
+      }
+    }
+ 
+    await transaction.commit();
+    res.status(200).json({ message: "Payment processed successfully" });
+  } catch (error) {
+    await transaction.rollback();
+    if (error instanceof AppError) {
+      res.status(error.statusCode).json({ message: error.message });
+    } else {
+      res.status(500).json({
+        message:
+          error instanceof Error
+            ? error.message
+            : "An error occurred during payment processing",
+      });
+    }
+  }
+};
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/farming-product-REST-api/src/controllers/product.controller.ts.html b/coverage/farming-product-REST-api/src/controllers/product.controller.ts.html new file mode 100644 index 0000000..4ab1233 --- /dev/null +++ b/coverage/farming-product-REST-api/src/controllers/product.controller.ts.html @@ -0,0 +1,943 @@ + + + + + + Code coverage report for farming-product-REST-api/src/controllers/product.controller.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api/src/controllers product.controller.ts

+
+ +
+ 7.89% + Statements + 18/228 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/6 +
+ + +
+ 7.89% + Lines + 18/228 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +2871x +1x +1x +1x +1x +1x +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +1x +1x +1x +1x +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import * as fs from "fs";
+import User from "../models/user";
+import Role from "../models/role";
+import BuyerReview from "../models/buyerreview";
+import Product from "../models/product";
+import { v2 as cloudinary } from "cloudinary";
+import { Request, Response, NextFunction } from "express";
+import { AuthenticatedRequest } from "../middleware/auth-check";
+import AppError from "../errors/customErrors";
+ 
+interface CloudinaryResponse {
+  secure_url: string;
+}
+ 
+interface CreateProductRequest {
+  productName: string;
+  description: string;
+  price: number;
+  quantity: number;
+  category: string;
+  imageUrl?: string;
+}
+ 
+type UpdateProductRequest = Partial<CreateProductRequest>;
+ 
+// Configure cloudinary
+cloudinary.config({
+  cloud_name: process.env.CLOUDINARY_CLOUD_NAME,
+  api_key: process.env.CLOUDINARY_API_KEY,
+  api_secret: process.env.CLOUDINARY_API_SECRET,
+});
+ 
+// Get all products
+export const allProducts = async (
+  req: Request,
+  res: Response,
+): Promise<void> => {
+  try {
+    const products = await Product.findAndCountAll({
+      include: [
+        {
+          model: User,
+          attributes: [
+            "id",
+            "firstName",
+            "lastName",
+            "country",
+            "verifiedUser",
+          ],
+          include: [{ model: Role }],
+        },
+      ],
+    });
+ 
+    if (!products) {
+      throw new AppError("No products found", 404);
+    }
+ 
+    res.status(200).json({ products });
+  } catch (error) {
+    if (error instanceof AppError) {
+      res.status(error.statusCode).json({ message: error.message });
+      return;
+    }
+    res.status(500).json({
+      message:
+        error instanceof Error ? error.message : "Error getting products",
+    });
+  }
+};
+ 
+// Get a product by ID
+export const getProduct = async (
+  req: Request<{ productId: string }>,
+  res: Response,
+): Promise<void> => {
+  try {
+    const foundProduct = await Product.findOne({
+      where: { id: req.params.productId },
+      include: [
+        {
+          model: User,
+          attributes: [
+            "id",
+            "firstName",
+            "lastName",
+            "country",
+            "imageUrl",
+            "verifiedUser",
+          ],
+        },
+        {
+          model: BuyerReview,
+          attributes: ["id", "comment", "rating", "createdAt"],
+          required: false,
+          include: [
+            {
+              model: User,
+              attributes: [
+                "id",
+                "firstName",
+                "lastName",
+                "country",
+                "imageUrl",
+                "verifiedUser",
+              ],
+            },
+          ],
+        },
+      ],
+    });
+ 
+    if (!foundProduct) {
+      throw new AppError("Product not found", 404);
+    }
+ 
+    res.status(200).json({ product: foundProduct });
+  } catch (error) {
+    if (error instanceof AppError) {
+      res.status(error.statusCode).json({ message: error.message });
+      return;
+    }
+    res.status(500).json({
+      message: error instanceof Error ? error.message : "Error getting product",
+    });
+  }
+};
+ 
+// Get all products of a user
+export const userProducts = async (
+  req: Request<{ userId: string }>,
+  res: Response,
+): Promise<void> => {
+  try {
+    const userProducts = await Product.findAndCountAll({
+      where: { userId: req.params.userId },
+    });
+ 
+    if (!userProducts) {
+      throw new AppError("No products found for this user", 404);
+    }
+ 
+    res.status(200).json({ products: userProducts });
+  } catch (error) {
+    if (error instanceof AppError) {
+      res.status(error.statusCode).json({ message: error.message });
+      return;
+    }
+    res.status(500).json({
+      message:
+        error instanceof Error ? error.message : "Error getting user products",
+    });
+  }
+};
+ 
+// Create a new product
+export const createProduct = async (
+  req: AuthenticatedRequest & { body: CreateProductRequest },
+  res: Response,
+  next: NextFunction,
+): Promise<void> => {
+  try {
+    if (!req.userData || typeof req.userData === "string") {
+      throw new AppError("Invalid authentication token", 401);
+    }
+ 
+    const { productName, description, price, quantity, category } = req.body;
+ 
+    // Validate required fields
+    if (!productName || !description || !price || !quantity || !category) {
+      throw new AppError("Missing required fields", 400);
+    }
+ 
+    const productData = {
+      ...req.body,
+      userId: req.userData.UserId,
+      createdAt: new Date(),
+      updatedAt: new Date(),
+    };
+ 
+    if (req.file) {
+      const cloudinaryResponse = (await cloudinary.uploader.upload(
+        req.file.path,
+        { resource_type: "image" },
+      )) as CloudinaryResponse;
+ 
+      productData.imageUrl = cloudinaryResponse.secure_url;
+ 
+      // Remove the file from public directory
+      fs.unlinkSync(req.file.path);
+    }
+ 
+    const result = await Product.create(productData);
+ 
+    res.status(201).json({
+      message: "Product created successfully",
+      product: result,
+    });
+  } catch (error) {
+    if (error instanceof AppError) {
+      res.status(error.statusCode).json({ message: error.message });
+      return;
+    }
+    next(error);
+  }
+};
+ 
+// Update a product
+export const updateProduct = async (
+  req: Request<{ productId: string }, unknown, UpdateProductRequest>,
+  res: Response,
+): Promise<void> => {
+  try {
+    const product = await Product.findByPk(req.params.productId);
+    if (!product) {
+      throw new AppError("Product not found", 404);
+    }
+ 
+    if (req.file) {
+      const cloudinaryResponse = (await cloudinary.uploader.upload(
+        req.file.path,
+      )) as CloudinaryResponse;
+ 
+      req.body.imageUrl = cloudinaryResponse.secure_url;
+ 
+      // Remove the file from public directory
+      fs.unlinkSync(req.file.path);
+    }
+ 
+    const productData = {
+      ...req.body,
+      updatedAt: new Date(),
+    };
+ 
+    const [updatedCount] = await Product.update(productData, {
+      where: { id: req.params.productId },
+    });
+ 
+    if (updatedCount === 0) {
+      throw new AppError("Failed to update product", 500);
+    }
+ 
+    res.status(200).json({ message: "Product updated successfully" });
+  } catch (error) {
+    if (error instanceof AppError) {
+      res.status(error.statusCode).json({ message: error.message });
+      return;
+    }
+    res.status(500).json({
+      message:
+        error instanceof Error ? error.message : "Error updating product",
+    });
+  }
+};
+ 
+// Delete a product
+export const removeProduct = async (
+  req: Request<{ productId: string }>,
+  res: Response,
+): Promise<void> => {
+  try {
+    const product = await Product.findByPk(req.params.productId);
+    if (!product) {
+      throw new AppError("Product not found", 404);
+    }
+ 
+    const deletedCount = await Product.destroy({
+      where: { id: req.params.productId },
+    });
+ 
+    if (deletedCount === 0) {
+      throw new AppError("Failed to delete product", 500);
+    }
+ 
+    res.status(200).json({ message: "Product has been deleted successfully" });
+  } catch (error) {
+    if (error instanceof AppError) {
+      res.status(error.statusCode).json({ message: error.message });
+      return;
+    }
+    res.status(500).json({
+      message:
+        error instanceof Error ? error.message : "Error deleting product",
+    });
+  }
+};
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/farming-product-REST-api/src/controllers/product.search.controller.ts.html b/coverage/farming-product-REST-api/src/controllers/product.search.controller.ts.html new file mode 100644 index 0000000..2d37969 --- /dev/null +++ b/coverage/farming-product-REST-api/src/controllers/product.search.controller.ts.html @@ -0,0 +1,553 @@ + + + + + + Code coverage report for farming-product-REST-api/src/controllers/product.search.controller.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api/src/controllers product.search.controller.ts

+
+ +
+ 4.95% + Statements + 6/121 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 4.95% + Lines + 6/121 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +1571x +1x +1x +1x +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import User from "../models/user";
+import BuyerReview from "../models/buyerreview";
+import Product from "../models/product";
+import { Op, WhereOptions } from "sequelize";
+import { Request, Response } from "express";
+import AppError from "../errors/customErrors";
+ 
+interface ProductSearchWhereClause {
+  productName?: { [Op.like]: string };
+  productCat?: { [Op.like]: string };
+  price?: { [Op.between]: [number, number] };
+  wholeSale?: boolean;
+}
+ 
+interface ProductSearchQuery {
+  productName?: string;
+  productCat?: string;
+  minPrice?: string;
+  maxPrice?: string;
+  productRating?: string;
+  wholeSale?: string;
+  page?: string;
+  limit?: string;
+}
+ 
+// Get all products with search and filters
+export const getAllProductSearch = async (
+  req: Request<unknown, unknown, unknown, ProductSearchQuery>,
+  res: Response,
+): Promise<void> => {
+  try {
+    const {
+      productName,
+      productCat = "All",
+      minPrice,
+      maxPrice,
+      productRating,
+      wholeSale,
+      page = "1",
+      limit = "10",
+    } = req.query;
+ 
+    // Validate pagination parameters
+    const pageNum = parseInt(page);
+    const limitNum = parseInt(limit);
+    if (isNaN(pageNum) || pageNum < 1) {
+      throw new AppError("Invalid page number", 400);
+    }
+    if (isNaN(limitNum) || limitNum < 1 || limitNum > 100) {
+      throw new AppError("Invalid limit value. Must be between 1 and 100", 400);
+    }
+ 
+    const offset: number = (pageNum - 1) * limitNum;
+    const whereClause: ProductSearchWhereClause = {};
+ 
+    // Build where clause based on query parameters
+    if (productName?.trim()) {
+      whereClause.productName = {
+        [Op.like]: `%${productName}%`,
+      };
+    }
+ 
+    if (productCat && productCat !== "All") {
+      whereClause.productCat = {
+        [Op.like]: `%${productCat}%`,
+      };
+    }
+ 
+    // Validate and add price range
+    const priceMin = minPrice ? parseInt(minPrice) : undefined;
+    const priceMax = maxPrice ? parseInt(maxPrice) : undefined;
+    if (priceMin !== undefined && priceMax !== undefined) {
+      if (isNaN(priceMin) || isNaN(priceMax)) {
+        throw new AppError("Invalid price range values", 400);
+      }
+      if (priceMin > priceMax) {
+        throw new AppError(
+          "Minimum price cannot be greater than maximum price",
+          400,
+        );
+      }
+      whereClause.price = {
+        [Op.between]: [priceMin, priceMax],
+      };
+    }
+ 
+    if (wholeSale === "true") {
+      whereClause.wholeSale = true;
+    }
+ 
+    // Validate rating
+    const rating = productRating ? parseInt(productRating) : 5;
+    if (isNaN(rating) || rating < 1 || rating > 5) {
+      throw new AppError("Invalid rating value. Must be between 1 and 5", 400);
+    }
+ 
+    const result = await Product.findAndCountAll({
+      where: whereClause as WhereOptions,
+      limit: limitNum,
+      offset,
+      include: [
+        {
+          model: User,
+          attributes: [
+            "id",
+            "firstName",
+            "lastName",
+            "country",
+            "verifiedUser",
+          ],
+        },
+        {
+          model: BuyerReview,
+          attributes: ["id", "comment", "rating"],
+          where: {
+            rating,
+          },
+          include: [
+            {
+              model: User,
+              attributes: [
+                "id",
+                "firstName",
+                "lastName",
+                "country",
+                "verifiedUser",
+              ],
+            },
+          ],
+          required: false,
+        },
+      ],
+    });
+ 
+    if (!result.count) {
+      res.status(200).json({
+        message: "No products found matching the search criteria",
+        queryResult: result,
+      });
+      return;
+    }
+ 
+    res.status(200).json({
+      queryResult: result,
+    });
+  } catch (error) {
+    if (error instanceof AppError) {
+      res.status(error.statusCode).json({ message: error.message });
+      return;
+    }
+    res.status(500).json({
+      message:
+        error instanceof Error ? error.message : "Error searching products",
+    });
+  }
+};
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/farming-product-REST-api/src/controllers/review.controller.ts.html b/coverage/farming-product-REST-api/src/controllers/review.controller.ts.html new file mode 100644 index 0000000..6282524 --- /dev/null +++ b/coverage/farming-product-REST-api/src/controllers/review.controller.ts.html @@ -0,0 +1,1153 @@ + + + + + + Code coverage report for farming-product-REST-api/src/controllers/review.controller.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api/src/controllers review.controller.ts

+
+ +
+ 4.77% + Statements + 13/272 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/5 +
+ + +
+ 4.77% + Lines + 13/272 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +335 +336 +337 +338 +339 +340 +341 +342 +343 +344 +345 +346 +347 +348 +349 +350 +351 +352 +353 +354 +355 +356 +3571x +1x +1x +1x +1x +  +1x +1x +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import User from "../models/user";
+import BuyerReview from "../models/buyerreview";
+import Product from "../models/product";
+import Order from "../models/order";
+import { Op, WhereOptions } from "sequelize";
+import { Request, Response } from "express";
+import sendPushNotificationToUser from "../middleware/send-notification";
+import handleExpoResponse, {
+  ExpoResponse,
+} from "../middleware/handleExpoResponse";
+import { AuthenticatedRequest } from "../middleware/auth-check";
+import AppError from "../errors/customErrors";
+ 
+interface ReviewWhereClause {
+  prodId: string;
+  rating?: { [Op.eq]: number };
+}
+ 
+interface PushNotificationMessage {
+  title: string;
+  text: string;
+}
+ 
+interface NotificationMessage {
+  title: string;
+  message: string;
+}
+ 
+interface CreateReviewRequest {
+  rating: number;
+  comment: string;
+}
+ 
+type UpdateReviewRequest = Partial<CreateReviewRequest>;
+ 
+// Get review of an order
+const orderReview = async (
+  req: Request<{ orderId: string }>,
+  res: Response,
+): Promise<void> => {
+  try {
+    const orderReview = await BuyerReview.findOne({
+      where: { orderId: req.params.orderId },
+      include: [
+        {
+          model: User,
+          attributes: [
+            "id",
+            "firstName",
+            "lastName",
+            "country",
+            "verifiedUser",
+          ],
+        },
+        {
+          model: Product,
+          attributes: ["id", "productName", "productCat", "imageUrl"],
+        },
+      ],
+    });
+ 
+    if (!orderReview) {
+      throw new AppError("Review not found for this order", 404);
+    }
+ 
+    res.status(200).json({
+      status: "success",
+      orderReviewData: orderReview,
+    });
+  } catch (error) {
+    if (error instanceof AppError) {
+      res.status(error.statusCode).json({ message: error.message });
+      return;
+    }
+    res.status(500).json({
+      message:
+        error instanceof Error
+          ? error.message
+          : "Error retrieving order review",
+    });
+  }
+};
+ 
+// Get reviews of a product
+const getReviewByProdId = async (
+  req: Request<{ productId: string }, unknown, unknown, { rating?: string }>,
+  res: Response,
+): Promise<void> => {
+  try {
+    const whereClause: ReviewWhereClause = {
+      prodId: req.params.productId,
+    };
+ 
+    const { rating } = req.query;
+    if (rating?.trim()) {
+      const ratingNum = Number(rating);
+      if (isNaN(ratingNum) || ratingNum < 1 || ratingNum > 5) {
+        throw new AppError(
+          "Invalid rating value. Must be between 1 and 5",
+          400,
+        );
+      }
+      whereClause.rating = {
+        [Op.eq]: ratingNum,
+      };
+    }
+ 
+    const reviews = await BuyerReview.findAndCountAll({
+      where: whereClause as unknown as WhereOptions,
+      include: [
+        {
+          model: User,
+          attributes: [
+            "id",
+            "firstName",
+            "lastName",
+            "country",
+            "verifiedUser",
+          ],
+        },
+        {
+          model: Product,
+          attributes: ["id", "productName", "productCat", "imageUrl"],
+        },
+      ],
+    });
+ 
+    if (!reviews.count) {
+      res.status(200).json({
+        message: "No reviews found for this product",
+        reviews,
+      });
+      return;
+    }
+ 
+    res.status(200).json({ reviews });
+  } catch (error) {
+    if (error instanceof AppError) {
+      res.status(error.statusCode).json({ message: error.message });
+      return;
+    }
+    res.status(500).json({
+      message:
+        error instanceof Error
+          ? error.message
+          : "Error retrieving product reviews",
+    });
+  }
+};
+ 
+// Create a review
+const createReview = async (
+  req: AuthenticatedRequest & {
+    body: CreateReviewRequest;
+    params: { productId: string; orderId: string };
+  },
+  res: Response,
+): Promise<void> => {
+  try {
+    if (!req.userData || typeof req.userData === "string") {
+      throw new AppError("Invalid authentication token", 401);
+    }
+ 
+    const { rating, comment } = req.body;
+ 
+    // Validate rating
+    if (!rating || rating < 1 || rating > 5) {
+      throw new AppError("Rating must be between 1 and 5", 400);
+    }
+ 
+    // Validate comment
+    if (!comment?.trim()) {
+      throw new AppError("Comment is required", 400);
+    }
+ 
+    const order = await Order.findOne({ where: { id: req.params.orderId } });
+    if (!order) {
+      throw new AppError("Order not found", 404);
+    }
+ 
+    if (order.status !== "delivered") {
+      throw new AppError(
+        "The order is still in processing or pending state. You cannot review yet",
+        401,
+      );
+    }
+ 
+    const reviewData = {
+      prodId: req.params.productId,
+      userId: req.userData.UserId,
+      orderId: req.params.orderId,
+      rating,
+      comment,
+      createdAt: new Date(),
+      updatedAt: new Date(),
+    };
+ 
+    const reviewOrder = { rating, comment };
+ 
+    await BuyerReview.create(reviewData);
+ 
+    await Order.update(
+      {
+        review: reviewOrder,
+        updatedAt: new Date(),
+      },
+      { where: { id: req.params.orderId } },
+    );
+ 
+    const sellerData = await User.findByPk(order.sellerId);
+ 
+    if (sellerData?.expoPushToken) {
+      const notificationMessage: NotificationMessage = {
+        title: "Order Reviewed",
+        message: "You got a review on your order from the buyer",
+      };
+ 
+      const pushMessage: PushNotificationMessage = {
+        title: notificationMessage.title,
+        text: notificationMessage.message,
+      };
+ 
+      const result = await sendPushNotificationToUser(
+        sellerData.expoPushToken,
+        pushMessage,
+      );
+ 
+      if (result && "status" in result) {
+        await handleExpoResponse(
+          result as ExpoResponse,
+          order.sellerId,
+          notificationMessage,
+        );
+      }
+    }
+ 
+    res.status(201).json({ message: "Review added successfully" });
+  } catch (error) {
+    if (error instanceof AppError) {
+      res.status(error.statusCode).json({ message: error.message });
+      return;
+    }
+    res.status(500).json({
+      message: error instanceof Error ? error.message : "Error creating review",
+    });
+  }
+};
+ 
+// Update a review
+const updateReview = async (
+  req: Request<{ reviewId: string }, unknown, UpdateReviewRequest>,
+  res: Response,
+): Promise<void> => {
+  try {
+    const { rating, comment } = req.body;
+ 
+    // Validate rating if provided
+    if (rating !== undefined && (rating < 1 || rating > 5)) {
+      throw new AppError("Rating must be between 1 and 5", 400);
+    }
+ 
+    // Validate comment if provided
+    if (comment !== undefined && !comment.trim()) {
+      throw new AppError("Comment cannot be empty", 400);
+    }
+ 
+    const review = await BuyerReview.findByPk(req.params.reviewId);
+    if (!review) {
+      throw new AppError("Review not found", 404);
+    }
+ 
+    const [updatedCount] = await BuyerReview.update(
+      {
+        ...req.body,
+        updatedAt: new Date(),
+      },
+      { where: { id: req.params.reviewId } },
+    );
+ 
+    if (updatedCount === 0) {
+      throw new AppError("Failed to update review", 500);
+    }
+ 
+    res.status(200).json({ message: "Review updated successfully" });
+  } catch (error) {
+    if (error instanceof AppError) {
+      res.status(error.statusCode).json({ message: error.message });
+      return;
+    }
+    res.status(500).json({
+      message: error instanceof Error ? error.message : "Error updating review",
+    });
+  }
+};
+ 
+// Delete own review
+const deleteOwnReview = async (
+  req: AuthenticatedRequest & { params: { reviewId: string } },
+  res: Response,
+): Promise<void> => {
+  try {
+    if (!req.userData || typeof req.userData === "string") {
+      throw new AppError("Invalid authentication token", 401);
+    }
+ 
+    const review = await BuyerReview.findOne({
+      where: { id: req.params.reviewId },
+      include: [
+        {
+          model: User,
+          attributes: [
+            "id",
+            "firstName",
+            "lastName",
+            "country",
+            "verifiedUser",
+          ],
+        },
+      ],
+    });
+ 
+    if (!review) {
+      throw new AppError("Review not found", 404);
+    }
+ 
+    if (review.userId !== req.userData.UserId) {
+      throw new AppError("You are not authorized to delete this review", 403);
+    }
+ 
+    const deletedCount = await BuyerReview.destroy({
+      where: { id: req.params.reviewId },
+    });
+ 
+    if (deletedCount === 0) {
+      throw new AppError("Failed to delete review", 500);
+    }
+ 
+    res.status(204).end();
+  } catch (error) {
+    if (error instanceof AppError) {
+      res.status(error.statusCode).json({ message: error.message });
+      return;
+    }
+    res.status(500).json({
+      message: error instanceof Error ? error.message : "Error deleting review",
+    });
+  }
+};
+ 
+export {
+  getReviewByProdId,
+  createReview,
+  updateReview,
+  deleteOwnReview,
+  orderReview,
+};
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/farming-product-REST-api/src/controllers/user.controller.ts.html b/coverage/farming-product-REST-api/src/controllers/user.controller.ts.html new file mode 100644 index 0000000..10cbe11 --- /dev/null +++ b/coverage/farming-product-REST-api/src/controllers/user.controller.ts.html @@ -0,0 +1,892 @@ + + + + + + Code coverage report for farming-product-REST-api/src/controllers/user.controller.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api/src/controllers user.controller.ts

+
+ +
+ 7.1% + Statements + 15/211 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/7 +
+ + +
+ 7.1% + Lines + 15/211 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +2701x +1x +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +1x +1x +1x +1x +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import User from "../models/user";
+import { hashSync, compare } from "bcryptjs";
+import { v2 as cloudinary } from "cloudinary";
+import { Request, Response, NextFunction } from "express";
+import { AuthenticatedRequest } from "../middleware/auth-check";
+ 
+interface CloudinaryResponse {
+  secure_url: string;
+}
+ 
+interface ShipAddress {
+  id: string;
+  title: string;
+  address: string;
+  default: boolean;
+}
+ 
+cloudinary.config({
+  cloud_name: process.env.CLOUDINARY_CLOUD_NAME,
+  api_key: process.env.CLOUDINARY_API_KEY,
+  api_secret: process.env.CLOUDINARY_API_SECRET,
+});
+ 
+// get all user data
+export const getAllUserData = async (
+  req: Request,
+  res: Response,
+): Promise<void> => {
+  try {
+    const users = await User.findAll();
+    if (!users?.length) {
+      res.status(400).json({ message: "No users found" });
+      return;
+    }
+ 
+    // remove password field from output
+    const usersWithoutPassword = users.map((user) => {
+      // eslint-disable-next-line @typescript-eslint/no-unused-vars
+      const { password, ...userData } = user.toJSON();
+      return userData;
+    });
+ 
+    res.status(200).json(usersWithoutPassword);
+  } catch (error) {
+    res.status(500).json({
+      message:
+        error instanceof Error ? error.message : "Error retrieving users",
+    });
+  }
+};
+ 
+// get a user data
+export const getUserData = async (
+  req: Request,
+  res: Response,
+): Promise<void> => {
+  const id = req.params.userId;
+ 
+  try {
+    const user = await User.findOne({ where: { id } });
+    if (!user) {
+      res.status(401).json({ message: "No such user found" });
+      return;
+    }
+ 
+    // eslint-disable-next-line @typescript-eslint/no-unused-vars
+    const { password, ...userData } = user.toJSON();
+ 
+    res.status(200).json(userData);
+  } catch (error) {
+    res.status(500).json({
+      message: error instanceof Error ? error.message : "Error retrieving user",
+    });
+  }
+};
+ 
+// Update a user's data
+export const updateUser = async (
+  req: AuthenticatedRequest,
+  res: Response,
+  next: NextFunction,
+): Promise<void> => {
+  const userId = req.params.userId;
+ 
+  try {
+    const user = await User.findByPk(userId);
+    if (!user) {
+      res.status(404).json({ message: "User not found" });
+      return;
+    }
+ 
+    if (req.file) {
+      const cloudinaryResponse = (await cloudinary.uploader.upload(
+        req.file.path,
+      )) as CloudinaryResponse;
+      req.body.imageUrl = cloudinaryResponse.secure_url;
+    }
+ 
+    const updatedUserData = {
+      ...req.body,
+      updatedAt: new Date(),
+    };
+ 
+    if (req.body.address && req.body.addressID) {
+      let shipAddress: ShipAddress[] = [];
+      const currentShipAddress =
+        typeof user.shipAddress === "string"
+          ? JSON.parse(user.shipAddress || "[]")
+          : (user.shipAddress as ShipAddress[]) || [];
+ 
+      shipAddress = Array.isArray(currentShipAddress) ? currentShipAddress : [];
+ 
+      const addressIndex = shipAddress.findIndex(
+        (addr) => addr.id === req.body.addressID,
+      );
+ 
+      if (addressIndex !== -1) {
+        shipAddress[addressIndex].address = req.body.address;
+        updatedUserData.shipAddress = shipAddress;
+      }
+    }
+ 
+    if (updatedUserData.password) {
+      updatedUserData.password = hashSync(updatedUserData.password, 10);
+    }
+ 
+    await User.update(updatedUserData, { where: { id: userId } });
+ 
+    const updatedUser = await User.findByPk(userId, {
+      attributes: { exclude: ["password"] },
+    });
+ 
+    res.status(200).json({
+      message: "Profile updated successfully",
+      userData: updatedUser,
+    });
+  } catch (error) {
+    next(error);
+  }
+};
+ 
+// delete a user
+export const deleteUser = async (
+  req: Request,
+  res: Response,
+): Promise<void> => {
+  try {
+    const userId = req.params.userId;
+ 
+    const existingUser = await User.findOne({ where: { id: userId } });
+    if (!existingUser) {
+      res.status(404).json({ message: "No such user found" });
+      return;
+    }
+ 
+    await User.destroy({ where: { id: userId } });
+    res.status(200).json({ message: "User deleted successfully" });
+  } catch (error) {
+    res.status(500).json({
+      message: error instanceof Error ? error.message : "Error deleting user",
+    });
+  }
+};
+ 
+// update password
+export const updatePassword = async (
+  req: Request,
+  res: Response,
+  next: NextFunction,
+): Promise<void> => {
+  const { password, userId, oldPassword } = req.body;
+ 
+  if (!password) {
+    res.status(400).json({
+      status: "FAILED",
+      message: "Empty input fields",
+    });
+    return;
+  }
+ 
+  if (password.length < 8) {
+    res.status(400).json({
+      status: "FAILED",
+      message: "Password must be at least 8 characters",
+    });
+    return;
+  }
+ 
+  try {
+    const userData = await User.findOne({ where: { id: userId } });
+    if (!userData) {
+      res.status(404).json({ message: "User not found" });
+      return;
+    }
+ 
+    if (oldPassword) {
+      const verifyPassword = await compare(
+        oldPassword,
+        userData.password as string,
+      );
+      if (!verifyPassword) {
+        res.status(403).json({
+          message:
+            "Current Password is incorrect. Please enter the correct current password",
+        });
+        return;
+      }
+    }
+ 
+    const hashedPassword = hashSync(password, 10);
+    userData.password = hashedPassword;
+    userData.updatedAt = new Date();
+ 
+    await User.update(userData, { where: { id: userId } });
+    res.status(200).json({ message: "Password successfully updated" });
+  } catch (error) {
+    next(error);
+  }
+};
+ 
+export const updateShipAddress = async (
+  req: Request,
+  res: Response,
+  next: NextFunction,
+): Promise<void> => {
+  const { userId } = req.params;
+ 
+  try {
+    await User.update(
+      {
+        shipAddress: req.body,
+        updatedAt: new Date(),
+      },
+      { where: { id: userId } },
+    );
+ 
+    const userData = await User.findOne({ where: { id: userId } });
+ 
+    res.status(200).json({
+      message: "Shipping address updated successfully",
+      data: userData,
+    });
+  } catch (error) {
+    next(error);
+  }
+};
+ 
+export const addExpoPushNotificationToken = async (
+  req: Request,
+  res: Response,
+  next: NextFunction,
+): Promise<void> => {
+  const { userId } = req.params;
+  const { expoPushToken } = req.body;
+ 
+  try {
+    await User.update(
+      {
+        expoPushToken,
+        updatedAt: new Date(),
+      },
+      { where: { id: userId } },
+    );
+ 
+    res.status(200).json({ message: "Push token saved successfully" });
+  } catch (error) {
+    next(error);
+  }
+};
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/farming-product-REST-api/src/errors/customErrors.ts.html b/coverage/farming-product-REST-api/src/errors/customErrors.ts.html new file mode 100644 index 0000000..7c7fe4e --- /dev/null +++ b/coverage/farming-product-REST-api/src/errors/customErrors.ts.html @@ -0,0 +1,136 @@ + + + + + + Code coverage report for farming-product-REST-api/src/errors/customErrors.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api/src/errors customErrors.ts

+
+ +
+ 100% + Statements + 10/10 +
+ + +
+ 100% + Branches + 3/3 +
+ + +
+ 100% + Functions + 1/1 +
+ + +
+ 100% + Lines + 10/10 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +181x +  +  +  +  +1x +5x +  +5x +5x +5x +  +5x +5x +1x +  +1x + 
class AppError extends Error {
+  public statusCode: number;
+  public status: string;
+  public isOperational: boolean;
+ 
+  constructor(message: string, statusCode: number) {
+    super(message);
+ 
+    this.statusCode = statusCode;
+    this.status = `${statusCode}`.startsWith("4") ? "fail" : "error";
+    this.isOperational = true;
+ 
+    Error.captureStackTrace(this, this.constructor);
+  }
+}
+ 
+export default AppError;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/farming-product-REST-api/src/errors/index.html b/coverage/farming-product-REST-api/src/errors/index.html new file mode 100644 index 0000000..0b52e8c --- /dev/null +++ b/coverage/farming-product-REST-api/src/errors/index.html @@ -0,0 +1,116 @@ + + + + + + Code coverage report for farming-product-REST-api/src/errors + + + + + + + + + +
+
+

All files farming-product-REST-api/src/errors

+
+ +
+ 100% + Statements + 10/10 +
+ + +
+ 100% + Branches + 3/3 +
+ + +
+ 100% + Functions + 1/1 +
+ + +
+ 100% + Lines + 10/10 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
customErrors.ts +
+
100%10/10100%3/3100%1/1100%10/10
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/farming-product-REST-api/src/middleware/auth-check.ts.html b/coverage/farming-product-REST-api/src/middleware/auth-check.ts.html new file mode 100644 index 0000000..6761324 --- /dev/null +++ b/coverage/farming-product-REST-api/src/middleware/auth-check.ts.html @@ -0,0 +1,349 @@ + + + + + + Code coverage report for farming-product-REST-api/src/middleware/auth-check.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api/src/middleware auth-check.ts

+
+ +
+ 6.77% + Statements + 4/59 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 6.77% + Lines + 4/59 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89  +1x +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x + 
import { Request, Response, NextFunction } from "express";
+import jwt, {
+  JwtPayload,
+  JsonWebTokenError,
+  TokenExpiredError,
+} from "jsonwebtoken";
+import AppError from "../errors/customErrors";
+ 
+interface DecodedToken extends JwtPayload {
+  UserId: string;
+}
+ 
+export interface AuthenticatedRequest extends Request {
+  userData?: DecodedToken;
+}
+ 
+const authCheck = async (
+  req: AuthenticatedRequest,
+  res: Response,
+  next: NextFunction,
+): Promise<void> => {
+  try {
+    // Check if authorization header is present
+    const authHeader = req.headers.authorization;
+    if (!authHeader) {
+      res.status(401).json({
+        message: "You are either not logged in or your session has expired",
+      });
+      return;
+    }
+ 
+    // Extract the token from the authorization header
+    const [bearer, token] = authHeader.split(" ");
+    if (bearer !== "Bearer" || !token) {
+      res.status(401).json({
+        message: "Invalid authorization header format",
+      });
+      return;
+    }
+ 
+    // Verify the token
+    const decodedToken = jwt.verify(
+      token,
+      process.env.JWT_SECRET as string,
+    ) as DecodedToken;
+ 
+    // Check if the token is expired
+    if (decodedToken.exp && decodedToken.exp < Date.now() / 1000) {
+      res.status(401).json({
+        auth: false,
+        message: "Your session has expired",
+      });
+      return;
+    }
+ 
+    // Attach decoded token data to the request object
+    req.userData = decodedToken;
+ 
+    // Verify the userId parameter matches the token's user ID, if applicable
+    if (req.params.userId && req.params.userId !== decodedToken.UserId) {
+      res.status(403).json({
+        message:
+          "You are not authorized for this, please log in using your account",
+      });
+      return;
+    }
+ 
+    // Proceed to the next middleware or route handler
+    next();
+  } catch (error) {
+    if (error instanceof TokenExpiredError) {
+      res.status(401).json({
+        message: "Your session has expired",
+      });
+      return;
+    }
+    if (error instanceof JsonWebTokenError) {
+      res.status(401).json({
+        message: "Invalid token",
+      });
+      return;
+    }
+    // Handle unexpected errors
+    next(new AppError("Authentication error", 500));
+  }
+};
+ 
+export default authCheck;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/farming-product-REST-api/src/middleware/errorHandler.ts.html b/coverage/farming-product-REST-api/src/middleware/errorHandler.ts.html new file mode 100644 index 0000000..ae8e484 --- /dev/null +++ b/coverage/farming-product-REST-api/src/middleware/errorHandler.ts.html @@ -0,0 +1,196 @@ + + + + + + Code coverage report for farming-product-REST-api/src/middleware/errorHandler.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api/src/middleware errorHandler.ts

+
+ +
+ 90.47% + Statements + 19/21 +
+ + +
+ 25% + Branches + 1/4 +
+ + +
+ 100% + Functions + 1/1 +
+ + +
+ 90.47% + Lines + 19/21 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38  +1x +  +  +  +  +  +  +  +1x +4x +4x +4x +4x +4x +4x +  +  +4x +4x +  +  +4x +4x +4x +4x +  +  +4x +  +  +  +4x +4x +4x +  +1x + 
import { Request, Response, NextFunction } from "express";
+import AppError from "../errors/customErrors";
+ 
+interface ErrorResponse {
+  status: string;
+  message: string;
+  stack?: string;
+}
+ 
+const errorHandler = (
+  err: Error | AppError,
+  _req: Request,
+  res: Response,
+  _next: NextFunction,
+): void => {
+  console.log("Error handler called:", err);
+ 
+  // Set default values
+  const statusCode = err instanceof AppError ? err.statusCode : 500;
+  const status = "fail"; // Always use 'fail' for consistency with tests
+ 
+  // Prepare error response
+  const errorResponse: ErrorResponse = {
+    status,
+    message: err.message || "An unexpected error occurred",
+  };
+ 
+  // Add stack trace in development environment
+  if (process.env.NODE_ENV === "development") {
+    errorResponse.stack = err.stack;
+  }
+ 
+  console.log("Sending error response:", errorResponse);
+  res.status(statusCode).json(errorResponse);
+};
+ 
+export default errorHandler;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/farming-product-REST-api/src/middleware/handleExpoResponse.ts.html b/coverage/farming-product-REST-api/src/middleware/handleExpoResponse.ts.html new file mode 100644 index 0000000..ef7bc95 --- /dev/null +++ b/coverage/farming-product-REST-api/src/middleware/handleExpoResponse.ts.html @@ -0,0 +1,340 @@ + + + + + + Code coverage report for farming-product-REST-api/src/middleware/handleExpoResponse.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api/src/middleware handleExpoResponse.ts

+
+ +
+ 10.86% + Statements + 5/46 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 10.86% + Lines + 5/46 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +861x +1x +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x + 
import Notification from "../models/notifiation";
+import User from "../models/user";
+import AppError from "../errors/customErrors";
+ 
+export interface ExpoResponse {
+  status: "ok" | "error";
+  details?: {
+    error?:
+      | "DeviceNotRegistered"
+      | "MessageTooBig"
+      | "InvalidCredentials"
+      | string;
+  };
+}
+ 
+interface MessageToSend {
+  title: string;
+  message: string;
+}
+ 
+interface NotificationData {
+  userId: string;
+  title: string;
+  message: string;
+  isRead: boolean;
+}
+ 
+/**
+ * Handles the response from Expo's push notification service
+ * @param result - The response from Expo's push notification service
+ * @param userId - The ID of the user to receive the notification
+ * @param messageToSend - The notification message details
+ * @throws {AppError} When there's an error creating the notification
+ */
+const expoNotificationResponse = async (
+  result: ExpoResponse,
+  userId: string,
+  messageToSend: MessageToSend,
+): Promise<void> => {
+  try {
+    if (result.status === "ok") {
+      const notificationData: NotificationData = {
+        userId,
+        title: messageToSend.title,
+        message: messageToSend.message,
+        isRead: false,
+      };
+ 
+      await Notification.create(notificationData);
+      return;
+    }
+ 
+    if (
+      result.status === "error" &&
+      result.details?.error === "DeviceNotRegistered"
+    ) {
+      // Remove the invalid push token from the user's record
+      const updateResult = await User.update(
+        { expoPushToken: null },
+        { where: { id: userId } },
+      );
+ 
+      // Check if the user was actually updated
+      if (updateResult[0] === 0) {
+        throw new AppError(`User not found with ID: ${userId}`, 404);
+      }
+      return;
+    }
+ 
+    // Handle other error cases
+    if (result.status === "error") {
+      throw new AppError(
+        `Expo notification error: ${result.details?.error || "Unknown error"}`,
+        500,
+      );
+    }
+  } catch (error) {
+    if (error instanceof AppError) {
+      throw error;
+    }
+    throw new AppError("Failed to process Expo notification response", 500);
+  }
+};
+ 
+export default expoNotificationResponse;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/farming-product-REST-api/src/middleware/index.html b/coverage/farming-product-REST-api/src/middleware/index.html new file mode 100644 index 0000000..7dc4ed3 --- /dev/null +++ b/coverage/farming-product-REST-api/src/middleware/index.html @@ -0,0 +1,191 @@ + + + + + + Code coverage report for farming-product-REST-api/src/middleware + + + + + + + + + +
+
+

All files farming-product-REST-api/src/middleware

+
+ +
+ 26.07% + Statements + 67/257 +
+ + +
+ 50% + Branches + 3/6 +
+ + +
+ 30% + Functions + 3/10 +
+ + +
+ 26.07% + Lines + 67/257 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
auth-check.ts +
+
6.77%4/59100%0/00%0/16.77%4/59
errorHandler.ts +
+
90.47%19/2125%1/4100%1/190.47%19/21
handleExpoResponse.ts +
+
10.86%5/46100%0/00%0/110.86%5/46
multerStorage.ts +
+
33.92%19/56100%1/125%1/433.92%19/56
rateLimiter.ts +
+
76.19%16/21100%1/150%1/276.19%16/21
send-notification.ts +
+
7.4%4/54100%0/00%0/17.4%4/54
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/farming-product-REST-api/src/middleware/multerStorage.ts.html b/coverage/farming-product-REST-api/src/middleware/multerStorage.ts.html new file mode 100644 index 0000000..92c4000 --- /dev/null +++ b/coverage/farming-product-REST-api/src/middleware/multerStorage.ts.html @@ -0,0 +1,310 @@ + + + + + + Code coverage report for farming-product-REST-api/src/middleware/multerStorage.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api/src/middleware multerStorage.ts

+
+ +
+ 33.92% + Statements + 19/56 +
+ + +
+ 100% + Branches + 1/1 +
+ + +
+ 25% + Functions + 1/4 +
+ + +
+ 33.92% + Lines + 19/56 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +761x +1x +  +1x +  +  +  +  +  +  +1x +1x +  +  +1x +  +  +  +  +  +  +1x +  +  +1x +1x +1x +1x +1x +1x +  +  +  +  +  +  +  +1x +  +  +1x +8x +  +8x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +8x +  +1x + 
import multer, { StorageEngine, MulterError, Multer } from "multer";
+import path from "path";
+import { Request, Response, NextFunction } from "express";
+import AppError from "../errors/customErrors";
+ 
+// Define custom types for multer request and response
+type MulterRequest = Request & { file?: Express.Multer.File };
+type MulterResponse = Response;
+ 
+// Define the storage configuration
+const storage: StorageEngine = multer.diskStorage({
+  destination: (_req, _file, cb) => {
+    cb(null, "./src/public/images");
+  },
+  filename: (_req, file, cb) => {
+    const uniqueSuffix = Date.now() + "-" + Math.round(Math.random() * 1e9);
+    cb(
+      null,
+      file.fieldname + "-" + uniqueSuffix + path.extname(file.originalname),
+    );
+  },
+});
+ 
+// Create multer instance with configuration
+const upload: Multer = multer({
+  storage,
+  limits: {
+    fileSize: 5 * 1024 * 1024, // 5MB max file size
+  },
+  fileFilter: (_req, file, cb) => {
+    // Accept only image files
+    if (!file.mimetype.startsWith("image")) {
+      cb(new Error("Only image files are allowed"));
+      return;
+    }
+    cb(null, true);
+  },
+});
+ 
+// Factory function to create the upload middleware
+const uploadMiddleware = (fieldName: string) => {
+  const middleware = upload.single(fieldName);
+ 
+  return (
+    req: MulterRequest,
+    res: MulterResponse,
+    next: NextFunction,
+  ): void => {
+    // Type assertion is necessary here due to multer's type incompatibility
+    const multerReq = req as unknown as Request;
+    const multerRes = res as unknown as Response;
+    const multerMiddleware = middleware as unknown as (
+      req: Request,
+      res: Response,
+      callback: (err: unknown) => void,
+    ) => void;
+    multerMiddleware(multerReq, multerRes, (err: unknown) => {
+      if (err instanceof MulterError) {
+        if (err.code === "LIMIT_FILE_SIZE") {
+          next(new AppError("File size too large. Maximum size is 5MB", 400));
+          return;
+        }
+        next(new AppError(err.message, 400));
+        return;
+      }
+      if (err instanceof Error) {
+        next(new AppError(err.message, 400));
+        return;
+      }
+      next();
+    });
+  };
+};
+ 
+export default uploadMiddleware;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/farming-product-REST-api/src/middleware/rateLimiter.ts.html b/coverage/farming-product-REST-api/src/middleware/rateLimiter.ts.html new file mode 100644 index 0000000..72aa3fb --- /dev/null +++ b/coverage/farming-product-REST-api/src/middleware/rateLimiter.ts.html @@ -0,0 +1,163 @@ + + + + + + Code coverage report for farming-product-REST-api/src/middleware/rateLimiter.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api/src/middleware rateLimiter.ts

+
+ +
+ 76.19% + Statements + 16/21 +
+ + +
+ 100% + Branches + 1/1 +
+ + +
+ 50% + Functions + 1/2 +
+ + +
+ 76.19% + Lines + 16/21 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +271x +1x +  +  +1x +1x +1x +1x +1x +1x +1x +1x +  +  +  +  +  +1x +  +4x +4x +1x +  +1x +  +1x + 
import { rateLimit, Options } from "express-rate-limit";
+import AppError from "../errors/customErrors";
+ 
+// Rate limiter configuration
+const rateLimitConfig: Partial<Options> = {
+  windowMs: 10 * 60 * 1000, // 10 minutes
+  max: 40, // Limit each IP to 40 requests per window
+  standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers
+  legacyHeaders: false, // Disable the `X-RateLimit-*` headers
+  message: "Too many requests from this IP, please try again later",
+  statusCode: 429, // Too Many Requests
+  handler: () => {
+    throw new AppError(
+      "Too many requests from this IP, please try again later",
+      429,
+    );
+  },
+  skip: (req) => {
+    // Skip rate limiting for health check endpoint
+    return req.path === "/health";
+  },
+};
+ 
+const limiter = rateLimit(rateLimitConfig);
+ 
+export default limiter;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/farming-product-REST-api/src/middleware/send-notification.ts.html b/coverage/farming-product-REST-api/src/middleware/send-notification.ts.html new file mode 100644 index 0000000..f254671 --- /dev/null +++ b/coverage/farming-product-REST-api/src/middleware/send-notification.ts.html @@ -0,0 +1,361 @@ + + + + + + Code coverage report for farming-product-REST-api/src/middleware/send-notification.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api/src/middleware send-notification.ts

+
+ +
+ 7.4% + Statements + 4/54 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 7.4% + Lines + 4/54 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +931x +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x + 
import fetch, { Response } from "node-fetch";
+import AppError from "../errors/customErrors";
+ 
+interface MessageToSend {
+  title: string;
+  text: string;
+}
+ 
+interface ExpoMessage {
+  to: string;
+  sound: "default" | "custom";
+  title: string;
+  body: string;
+  data?: Record<string, unknown>;
+  priority?: "default" | "normal" | "high";
+}
+ 
+interface ExpoResponse {
+  data: {
+    status: "ok" | "error";
+    id: string;
+    message: string;
+  }[];
+}
+ 
+/**
+ * Sends a push notification to a user via Expo's push notification service
+ * @param pushToken - The user's Expo push token
+ * @param messageToSend - The notification message details
+ * @returns The response from Expo's push notification service
+ * @throws {AppError} When the notification fails to send
+ */
+const sendPushNotificationToUser = async (
+  pushToken: string | null,
+  messageToSend: MessageToSend,
+): Promise<ExpoResponse | undefined> => {
+  if (!pushToken) {
+    return undefined;
+  }
+ 
+  const message: ExpoMessage = {
+    to: pushToken,
+    sound: "default",
+    title: messageToSend.title,
+    body: messageToSend.text,
+    priority: "high",
+  };
+ 
+  try {
+    const response: Response = await fetch(
+      "https://exp.host/--/api/v2/push/send",
+      {
+        method: "POST",
+        headers: {
+          Accept: "application/json",
+          "Content-Type": "application/json",
+        },
+        body: JSON.stringify(message),
+      },
+    );
+ 
+    if (!response.ok) {
+      throw new AppError(
+        `Failed to send notification: ${response.statusText}`,
+        response.status,
+      );
+    }
+ 
+    const result = (await response.json()) as ExpoResponse;
+ 
+    // Check for errors in the response
+    const error = result.data?.find((item) => item.status === "error");
+    if (error) {
+      throw new AppError(`Expo notification error: ${error.message}`, 500);
+    }
+ 
+    return result;
+  } catch (error) {
+    if (error instanceof AppError) {
+      throw error;
+    }
+    if (error instanceof Error) {
+      throw new AppError(
+        `Failed to send push notification: ${error.message}`,
+        500,
+      );
+    }
+    throw new AppError("Failed to send push notification", 500);
+  }
+};
+ 
+export default sendPushNotificationToUser;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/farming-product-REST-api/src/models/buyerreview.ts.html b/coverage/farming-product-REST-api/src/models/buyerreview.ts.html new file mode 100644 index 0000000..9a44e8b --- /dev/null +++ b/coverage/farming-product-REST-api/src/models/buyerreview.ts.html @@ -0,0 +1,298 @@ + + + + + + Code coverage report for farming-product-REST-api/src/models/buyerreview.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api/src/models buyerreview.ts

+
+ +
+ 100% + Statements + 52/52 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 100% + Functions + 0/0 +
+ + +
+ 100% + Lines + 52/52 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +721x +  +  +  +  +  +  +  +  +  +  +1x +1x +1x +  +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +  +1x +1x +1x +1x +1x +  +1x +1x +1x +1x +1x +  +1x +1x +1x +1x +1x +  +1x +1x +  +1x +1x +  +1x +1x +  +1x +1x +1x +1x +1x +  +1x + 
import {
+  Table,
+  Column,
+  Model,
+  DataType,
+  CreatedAt,
+  UpdatedAt,
+  ForeignKey,
+  BelongsTo,
+} from "sequelize-typescript";
+import { BuyerReviewAttributes } from "../types";
+import Product from "./product";
+import User from "./user";
+import Order from "./order";
+ 
+@Table({
+  timestamps: true,
+  tableName: "BuyerReviews",
+  modelName: "BuyerReview",
+})
+class BuyerReview extends Model<BuyerReviewAttributes> {
+  @Column({
+    primaryKey: true,
+    type: DataType.UUID,
+    defaultValue: DataType.UUIDV4,
+    allowNull: false,
+  })
+  declare id: string;
+  @Column({
+    type: DataType.STRING,
+  })
+  declare comment: string;
+  @Column({
+    type: DataType.INTEGER,
+  })
+  declare rating: number;
+ 
+  @ForeignKey(() => User)
+  @Column({
+    type: DataType.UUID,
+  })
+  userId!: string;
+ 
+  @ForeignKey(() => Product)
+  @Column({
+    type: DataType.UUID,
+  })
+  prodId!: string;
+ 
+  @ForeignKey(() => Order)
+  @Column({
+    type: DataType.UUID,
+  })
+  orderId!: string;
+ 
+  @BelongsTo(() => User)
+  user?: User;
+ 
+  @BelongsTo(() => Product)
+  product?: Product;
+ 
+  @BelongsTo(() => Order)
+  order?: Order;
+ 
+  @CreatedAt
+  declare createdAt: Date;
+  @UpdatedAt
+  declare updatedAt: Date;
+}
+ 
+export default BuyerReview;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/farming-product-REST-api/src/models/index.html b/coverage/farming-product-REST-api/src/models/index.html new file mode 100644 index 0000000..ba85a53 --- /dev/null +++ b/coverage/farming-product-REST-api/src/models/index.html @@ -0,0 +1,236 @@ + + + + + + Code coverage report for farming-product-REST-api/src/models + + + + + + + + + +
+
+

All files farming-product-REST-api/src/models

+
+ +
+ 73.21% + Statements + 440/601 +
+ + +
+ 83.33% + Branches + 5/6 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 73.21% + Lines + 440/601 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
buyerreview.ts +
+
100%52/52100%0/0100%0/0100%52/52
index.ts +
+
0%0/1610%0/10%0/10%0/161
notifiation.ts +
+
100%41/41100%0/0100%0/0100%41/41
order.ts +
+
100%77/77100%0/0100%0/0100%77/77
product.ts +
+
100%62/62100%0/0100%0/0100%62/62
role.ts +
+
100%23/23100%0/0100%0/0100%23/23
transaction.ts +
+
100%52/52100%0/0100%0/0100%52/52
user.ts +
+
100%96/96100%5/5100%0/0100%96/96
userotpcode.ts +
+
100%37/37100%0/0100%0/0100%37/37
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/farming-product-REST-api/src/models/index.ts.html b/coverage/farming-product-REST-api/src/models/index.ts.html new file mode 100644 index 0000000..52164e7 --- /dev/null +++ b/coverage/farming-product-REST-api/src/models/index.ts.html @@ -0,0 +1,826 @@ + + + + + + Code coverage report for farming-product-REST-api/src/models/index.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api/src/models index.ts

+
+ +
+ 0% + Statements + 0/161 +
+ + +
+ 0% + Branches + 0/1 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 0% + Lines + 0/161 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import dotenv from "dotenv";
+dotenv.config();
+import { join } from "path";
+import fs from "fs";
+import { Transaction, QueryInterface } from "sequelize";
+import { Sequelize } from "sequelize-typescript";
+import runMigrations from "../utils/runMigrations";
+import config from "../config/config";
+ 
+interface SequelizeTypeMap {
+  [key: string]: string[];
+}
+ 
+interface SequelizeType {
+  constructor: {
+    key: string;
+  };
+}
+ 
+interface ModelAttribute {
+  type: SequelizeType;
+  [key: string]: unknown;
+}
+ 
+const environment = process.env?.NODE_ENV || "production";
+const envConfig = config[environment as keyof typeof config];
+ 
+if (!envConfig) {
+  throw new Error(`No configuration found for environment: ${environment}`);
+}
+ 
+//external connection string
+const connectionString = process.env.DATABASE_URL;
+ 
+// Function to dynamically load models
+const loadModels = (): string[] => {
+  const modelsDir = __dirname;
+  try {
+    const modelFiles = fs.readdirSync(modelsDir);
+    return modelFiles
+      .filter(
+        (file) =>
+          (file.endsWith(".ts") || file.endsWith(".js")) &&
+          !file.startsWith("index"),
+      )
+      .map((file) => join(modelsDir, file));
+  } catch (error) {
+    console.error("Error loading models:", error);
+    throw new Error("Failed to load models.");
+  }
+};
+ 
+const models = loadModels();
+if (models.length === 0) {
+  throw new Error("No models found in the models directory.");
+}
+ 
+const sequelize = new Sequelize(`${connectionString}`, {
+  dialect: "postgres",
+  dialectOptions: {
+    ssl: {
+      require: true,
+      rejectUnauthorized: false, // Use this we use a service that uses a self-signed certificate
+    },
+  },
+  logging: console.log, // Set to false to disable SQL query logging
+  models: models,
+});
+ 
+/**
+ * Asynchronous (IIFE) for database synchronization and migration.
+ *
+ * This function performs the following tasks:
+ * 1. In development environment, it alters the database structure.
+ * 2. In production environment, it checks for schema, field, data-type changes and runs migrations if necessary.
+ * 3. Handles errors differently based on the environment.
+ *
+ * @async
+ * @function
+ * @throws {Error} Throws an error if database synchronization fails.
+ * @returns {Promise<void>} A promise that resolves when the database operations are complete.
+ */
+(async () => {
+  try {
+    if (environment === "development") {
+      console.log("Altering the database...");
+      await sequelize.sync({ alter: true });
+      console.log("✅ Database altered successfully.");
+    } else if (
+      environment === "production" &&
+      process.env.RUN_MIGRATIONS === "true"
+    ) {
+      // Use Sequelize's built-in schema comparison instead of raw SQL
+      console.log("Checking for schema changes...");
+ 
+      // Create a transaction to ensure consistency
+      const transaction: Transaction = await sequelize.transaction();
+ 
+      try {
+        // Use Sequelize's queryInterface for safer schema operations
+        const queryInterface: QueryInterface = sequelize.getQueryInterface();
+ 
+        // Track if we need to run migrations
+        let hasSchemaChanges = false;
+ 
+        // Check each model for changes
+        for (const modelName in sequelize.models) {
+          const model = sequelize.models[modelName] as unknown as {
+            tableName: string;
+            rawAttributes: Record<string, ModelAttribute>;
+          };
+          const tableName = model.tableName;
+ 
+          // Check if table exists
+          const tableExists = await queryInterface.tableExists(tableName, {
+            transaction,
+          });
+ 
+          if (!tableExists) {
+            console.log(`Schema change detected: New table ${tableName}`);
+            hasSchemaChanges = true;
+            break;
+          }
+ 
+          // Get current table description
+          const tableDescription = await queryInterface.describeTable(
+            tableName,
+            {},
+          );
+ 
+          // Get model attributes
+          const modelAttributes = model.rawAttributes;
+ 
+          // Check for new or modified columns
+          for (const attributeName in modelAttributes) {
+            const attribute = modelAttributes[attributeName];
+ 
+            // Skip virtual fields
+            if (attribute.type.constructor.key === "VIRTUAL") continue;
+ 
+            // Get the column info from database
+            const columnInfo = tableDescription[attributeName];
+ 
+            // If column doesn't exist, it's a new column
+            if (!columnInfo) {
+              console.log(
+                `Schema change detected: New column ${tableName}.${attributeName}`,
+              );
+              hasSchemaChanges = true;
+              break;
+            }
+ 
+            const sequelizeType = attribute.type.constructor.key;
+            const dbType = columnInfo.type;
+ 
+            // Check type compatibility
+            if (!isTypeCompatible(sequelizeType, dbType)) {
+              console.log(
+                `Schema change detected: Type change for ${tableName}.${attributeName}`,
+              );
+              hasSchemaChanges = true;
+              break;
+            }
+          }
+ 
+          if (hasSchemaChanges) break;
+ 
+          // Check for removed columns
+          for (const columnName in tableDescription) {
+            if (
+              !(columnName in modelAttributes) &&
+              !["id", "createdAt", "updatedAt"].includes(columnName)
+            ) {
+              // Skip standard Sequelize fields
+              console.log(
+                `Schema change detected: Removed column ${tableName}.${columnName}`,
+              );
+              hasSchemaChanges = true;
+              break;
+            }
+          }
+ 
+          if (hasSchemaChanges) break;
+        }
+ 
+        // Only run migrations if schema changes are detected
+        if (hasSchemaChanges) {
+          await transaction.commit();
+          console.log("Running migrations due to schema changes...");
+          await runMigrations(sequelize);
+          console.log("✅ Migrations completed successfully.");
+        } else {
+          await transaction.commit();
+          console.log("No schema changes detected. Skipping migrations.");
+        }
+      } catch (err) {
+        // Rollback transaction on error
+        await transaction.rollback();
+        throw err;
+      }
+    } else {
+      console.log("✅ Database is up to date.");
+    }
+  } catch (error) {
+    console.error("Error during database synchronization:", error);
+    // More graceful error handling for production
+    if (environment === "production") {
+      console.error(
+        "Database sync failed, but application will continue running with existing schema",
+      );
+      // Consider sending an alert to your monitoring system here
+    } else {
+      process.exit(1); // Only exit in development
+    }
+  }
+})();
+ 
+// Helper function to check type compatibility
+function isTypeCompatible(sequelizeType: string, dbType: string): boolean {
+  // This is a simplified version - expand based on your database and types
+  const typeMap: SequelizeTypeMap = {
+    STRING: ["character varying", "varchar", "text"],
+    INTEGER: ["integer", "int", "int4"],
+    BIGINT: ["bigint", "int8"],
+    FLOAT: ["real", "float4"],
+    DOUBLE: ["double precision", "float8"],
+    DECIMAL: ["numeric", "decimal"],
+    BOOLEAN: ["boolean", "bool"],
+    DATE: ["timestamp", "timestamptz", "date"],
+    DATEONLY: ["date"],
+    UUID: ["uuid"],
+    JSON: ["json", "jsonb"],
+    JSONB: ["jsonb"],
+    ARRAY: ["array"],
+  };
+ 
+  // Convert types to lowercase for comparison
+  const normalizedSequelizeType = sequelizeType.toUpperCase();
+  const normalizedDbType = dbType.toLowerCase();
+ 
+  // Check if the database type is compatible with the Sequelize type
+  return typeMap[normalizedSequelizeType]
+    ? typeMap[normalizedSequelizeType].includes(normalizedDbType)
+    : false;
+}
+ 
+export default sequelize;
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/farming-product-REST-api/src/models/notifiation.ts.html b/coverage/farming-product-REST-api/src/models/notifiation.ts.html new file mode 100644 index 0000000..259525f --- /dev/null +++ b/coverage/farming-product-REST-api/src/models/notifiation.ts.html @@ -0,0 +1,268 @@ + + + + + + Code coverage report for farming-product-REST-api/src/models/notifiation.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api/src/models notifiation.ts

+
+ +
+ 100% + Statements + 41/41 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 100% + Functions + 0/0 +
+ + +
+ 100% + Lines + 41/41 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +1x +1x +1x +1x +1x +1x +  +  +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +  +1x +1x +  +1x +1x +1x +1x +1x +  +1x + 
"use strict";
+import {
+  Table,
+  Column,
+  Model,
+  DataType,
+  CreatedAt,
+  UpdatedAt,
+  ForeignKey,
+  BelongsTo,
+} from "sequelize-typescript";
+import {
+  NotificationAttributes,
+  NotificationCreationAttributes,
+} from "../types";
+import User from "./user";
+ 
+@Table({
+  timestamps: true,
+  tableName: "Notifications",
+  modelName: "Notification",
+})
+class Notification extends Model<
+  NotificationAttributes,
+  NotificationCreationAttributes
+> {
+  @Column({
+    primaryKey: true,
+    type: DataType.UUID,
+    defaultValue: DataType.UUIDV4,
+    allowNull: false,
+  })
+  declare id: string; // UUID
+  @Column({
+    type: DataType.STRING,
+  })
+  declare title?: string;
+  @Column({
+    type: DataType.STRING,
+  })
+  declare message?: string;
+  @Column({
+    type: DataType.BOOLEAN,
+  })
+  declare isRead: boolean;
+  @ForeignKey(() => User)
+  @Column({
+    type: DataType.UUID,
+  })
+  userId!: string; // UUID
+ 
+  @BelongsTo(() => User)
+  user!: User;
+ 
+  @CreatedAt
+  declare createdAt: Date;
+  @UpdatedAt
+  declare updatedAt: Date;
+}
+ 
+export default Notification;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/farming-product-REST-api/src/models/order.ts.html b/coverage/farming-product-REST-api/src/models/order.ts.html new file mode 100644 index 0000000..e04911e --- /dev/null +++ b/coverage/farming-product-REST-api/src/models/order.ts.html @@ -0,0 +1,382 @@ + + + + + + Code coverage report for farming-product-REST-api/src/models/order.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api/src/models order.ts

+
+ +
+ 100% + Statements + 77/77 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 100% + Functions + 0/0 +
+ + +
+ 100% + Lines + 77/77 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100  +1x +  +  +  +  +  +  +  +  +  +  +  +1x +1x +1x +1x +  +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +  +1x +1x +1x +1x +1x +  +1x +1x +1x +  +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +  +1x +1x +1x +1x +  +1x +1x +  +1x +1x +  +1x +1x +  +1x +1x +1x +1x +1x +  +1x + 
"use strict";
+import {
+  Table,
+  Column,
+  Model,
+  DataType,
+  CreatedAt,
+  UpdatedAt,
+  ForeignKey,
+  BelongsTo,
+  HasOne,
+} from "sequelize-typescript";
+import { OrderAttributes } from "../types";
+import User from "./user";
+import BuyerReview from "./buyerreview";
+import Product from "./product";
+import Transaction from "./transaction";
+ 
+@Table({
+  timestamps: true,
+  tableName: "Orders",
+  modelName: "Order",
+})
+class Order extends Model<OrderAttributes> {
+  @Column({
+    primaryKey: true,
+    type: DataType.UUID,
+    defaultValue: DataType.UUIDV4,
+    allowNull: false,
+  })
+  declare id: string; // UUID
+  @Column({
+    type: DataType.INTEGER,
+  })
+  declare amount?: number;
+  @Column({
+    type: DataType.STRING,
+  })
+  declare shipAddress?: string;
+  @Column({
+    type: DataType.STRING,
+  })
+  declare weight?: string;
+  @Column({
+    type: DataType.JSON,
+  })
+  declare review?: object | null;
+  @Column({
+    type: DataType.ENUM("pending", "processing", "delivered", "dispatched"),
+  })
+  declare status: string;
+ 
+  @ForeignKey(() => Product)
+  @Column({
+    type: DataType.UUID,
+  })
+  prodId!: string;
+ 
+  @ForeignKey(() => User)
+  @Column({ type: DataType.UUID })
+  buyerId!: string; // UUID
+ 
+  @ForeignKey(() => User)
+  @Column({ type: DataType.UUID })
+  sellerId!: string; // UUID
+  @Column({
+    type: DataType.JSON,
+  })
+  declare dispatchDetails?: object | null;
+  @Column({
+    type: DataType.BOOLEAN,
+  })
+  declare dispatched: boolean;
+  @Column({
+    type: DataType.DATE,
+  })
+  declare deliveryDate?: Date | null;
+ 
+  @HasOne(() => BuyerReview)
+  buyerReview?: BuyerReview; // UUID
+  @HasOne(() => Transaction)
+  transaction?: Transaction;
+ 
+  @BelongsTo(() => User, "buyerId")
+  buyer!: User;
+ 
+  @BelongsTo(() => User, "sellerId")
+  seller!: User;
+ 
+  @BelongsTo(() => Product, "prodId")
+  product!: Product;
+ 
+  @CreatedAt
+  declare createdAt: Date;
+  @UpdatedAt
+  declare updatedAt: Date;
+}
+ 
+export default Order;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/farming-product-REST-api/src/models/product.ts.html b/coverage/farming-product-REST-api/src/models/product.ts.html new file mode 100644 index 0000000..5a4ee62 --- /dev/null +++ b/coverage/farming-product-REST-api/src/models/product.ts.html @@ -0,0 +1,352 @@ + + + + + + Code coverage report for farming-product-REST-api/src/models/product.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api/src/models product.ts

+
+ +
+ 100% + Statements + 62/62 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 100% + Functions + 0/0 +
+ + +
+ 100% + Lines + 62/62 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90  +1x +  +  +  +  +  +  +  +  +  +  +  +  +1x +1x +1x +  +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +  +1x +1x +1x +1x +  +1x +1x +1x +1x +  +1x +1x +1x +1x +  +1x +1x +1x +1x +  +1x +1x +1x +1x +  +1x +1x +1x +1x +  +1x +1x +1x +1x +  +1x +1x +1x +1x +1x +  +1x +1x +  +1x +1x +  +1x +1x +  +1x +1x +1x +1x +1x +  +1x + 
"use strict";
+import {
+  Table,
+  Column,
+  Model,
+  DataType,
+  CreatedAt,
+  UpdatedAt,
+  ForeignKey,
+  BelongsTo,
+  HasMany,
+} from "sequelize-typescript";
+import { ProductAttributes } from "../types";
+ 
+import User from "./user";
+import BuyerReview from "./buyerreview";
+import Order from "./order";
+ 
+@Table({
+  timestamps: true,
+  tableName: "Products",
+  modelName: "Product",
+})
+class Product extends Model<ProductAttributes> {
+  @Column({
+    primaryKey: true,
+    type: DataType.UUID,
+    defaultValue: DataType.UUIDV4,
+    allowNull: false,
+  })
+  declare id: string; // UUID
+ 
+  @Column({
+    type: DataType.STRING,
+  })
+  declare productName?: string;
+ 
+  @Column({
+    type: DataType.STRING,
+  })
+  declare productCat?: string;
+ 
+  @Column({
+    type: DataType.STRING,
+  })
+  declare priceType?: string;
+ 
+  @Column({
+    type: DataType.FLOAT,
+  })
+  declare price?: number;
+ 
+  @Column({
+    type: DataType.STRING,
+  })
+  declare imageUrl?: string;
+ 
+  @Column({
+    type: DataType.STRING,
+  })
+  declare description?: string;
+ 
+  @Column({
+    type: DataType.BOOLEAN,
+  })
+  declare wholeSale?: boolean;
+ 
+  @ForeignKey(() => User)
+  @Column({
+    type: DataType.UUID,
+  })
+  userId!: string; // UUID
+ 
+  @BelongsTo(() => User)
+  user!: User;
+ 
+  @HasMany(() => BuyerReview)
+  buyerReview?: BuyerReview[];
+ 
+  @HasMany(() => Order)
+  order?: Order[];
+ 
+  @CreatedAt
+  declare createdAt: Date;
+  @UpdatedAt
+  declare updatedAt: Date;
+}
+ 
+export default Product;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/farming-product-REST-api/src/models/role.ts.html b/coverage/farming-product-REST-api/src/models/role.ts.html new file mode 100644 index 0000000..44bffcc --- /dev/null +++ b/coverage/farming-product-REST-api/src/models/role.ts.html @@ -0,0 +1,169 @@ + + + + + + Code coverage report for farming-product-REST-api/src/models/role.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api/src/models role.ts

+
+ +
+ 100% + Statements + 23/23 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 100% + Functions + 0/0 +
+ + +
+ 100% + Lines + 23/23 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +291x +  +1x +  +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +  +1x +1x +1x +1x +  +1x +1x +1x +  +1x + 
import { Table, Column, Model, DataType, HasMany } from "sequelize-typescript";
+import { RoleAttributes } from "../types";
+import User from "./user";
+ 
+@Table({
+  timestamps: true,
+  tableName: "Roles",
+  modelName: "Role",
+})
+class Role extends Model<RoleAttributes> {
+  @Column({
+    primaryKey: true,
+    type: DataType.UUID,
+    defaultValue: DataType.UUIDV4,
+    allowNull: false,
+  })
+  declare id: string;
+ 
+  @Column({
+    type: DataType.STRING,
+  })
+  declare roleName: "farmer" | "buyer";
+ 
+  @HasMany(() => User, "roleId")
+  user?: User[];
+}
+ 
+export default Role;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/farming-product-REST-api/src/models/transaction.ts.html b/coverage/farming-product-REST-api/src/models/transaction.ts.html new file mode 100644 index 0000000..4791e03 --- /dev/null +++ b/coverage/farming-product-REST-api/src/models/transaction.ts.html @@ -0,0 +1,280 @@ + + + + + + Code coverage report for farming-product-REST-api/src/models/transaction.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api/src/models transaction.ts

+
+ +
+ 100% + Statements + 52/52 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 100% + Functions + 0/0 +
+ + +
+ 100% + Lines + 52/52 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +661x +  +  +  +  +  +  +  +  +  +1x +  +  +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +  +1x +1x +1x +1x +1x +1x +1x +  +1x + 
import {
+  Table,
+  Column,
+  Model,
+  DataType,
+  CreatedAt,
+  UpdatedAt,
+  ForeignKey,
+  BelongsTo,
+} from "sequelize-typescript";
+import Order from "./order";
+import { TransactionAttributes } from "../types";
+ 
+@Table({
+  timestamps: true,
+  tableName: "Transactions",
+  modelName: "Transaction",
+})
+class Transaction extends Model<TransactionAttributes> {
+  @Column({
+    primaryKey: true,
+    type: DataType.UUID,
+    defaultValue: DataType.UUIDV4,
+    allowNull: false,
+  })
+  declare id: string; // UUID
+  @Column({
+    type: DataType.INTEGER,
+  })
+  declare amount?: number;
+  @Column({
+    type: DataType.ENUM("pending", "completed", "rejected"),
+  })
+  declare status: string;
+  @Column({
+    type: DataType.ENUM("Payment", "Refund"),
+  })
+  declare txType: string;
+  @Column({
+    type: DataType.ENUM("MOBILE-MONEY", "ORANGE-MONEY", "VISA", "MASTERCARD"),
+  })
+  declare txMethod?: string;
+  @Column({
+    type: DataType.JSON,
+  })
+  declare txDetails?: object | null;
+  @Column({
+    type: DataType.STRING,
+  })
+  declare currency: string;
+  @ForeignKey(() => Order)
+  @Column({
+    type: DataType.UUID,
+  })
+  orderId!: string; // UUID
+ 
+  @BelongsTo(() => Order)
+  order!: Order;
+  @CreatedAt
+  declare createdAt: Date;
+  @UpdatedAt
+  declare updatedAt: Date;
+}
+ 
+export default Transaction;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/farming-product-REST-api/src/models/user.ts.html b/coverage/farming-product-REST-api/src/models/user.ts.html new file mode 100644 index 0000000..b8a3582 --- /dev/null +++ b/coverage/farming-product-REST-api/src/models/user.ts.html @@ -0,0 +1,433 @@ + + + + + + Code coverage report for farming-product-REST-api/src/models/user.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api/src/models user.ts

+
+ +
+ 100% + Statements + 96/96 +
+ + +
+ 100% + Branches + 5/5 +
+ + +
+ 100% + Functions + 0/0 +
+ + +
+ 100% + Lines + 96/96 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +1171x +  +  +  +  +  +  +  +  +  +  +  +1x +1x +1x +1x +  +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +  +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +  +1x +1x +  +1x +1x +  +1x +1x +  +1x +1x +  +1x +1x +  +1x +1x +1x +1x +1x +  +1x + 
import {
+  Table,
+  Column,
+  Model,
+  DataType,
+  CreatedAt,
+  UpdatedAt,
+  ForeignKey,
+  HasMany,
+  BelongsTo,
+} from "sequelize-typescript";
+import { UserAttributes } from "../types";
+import Role from "./role";
+import Order from "./order";
+import BuyerReview from "./buyerreview";
+import Product from "./product";
+ 
+@Table({
+  timestamps: true,
+  tableName: "Users",
+  modelName: "User",
+})
+class User extends Model<UserAttributes> {
+  @Column({
+    primaryKey: true,
+    type: DataType.UUID,
+    defaultValue: DataType.UUIDV4,
+    allowNull: false,
+  })
+  declare id: string;
+  @Column({
+    type: DataType.STRING,
+  })
+  declare firstName: string;
+  @Column({
+    type: DataType.STRING,
+  })
+  declare lastName: string;
+  @Column({
+    type: DataType.STRING,
+  })
+  declare email: string;
+  @Column({
+    type: DataType.STRING,
+  })
+  declare address: string;
+  @Column({
+    type: DataType.JSON,
+    defaultValue: [],
+  })
+  declare shipAddress: object[];
+  @Column({
+    type: DataType.STRING,
+  })
+  declare country: string;
+  @Column({
+    type: DataType.STRING,
+  })
+  declare imageUrl: string;
+  @Column({
+    type: DataType.STRING,
+  })
+  declare googleId: string;
+  @Column({
+    type: DataType.STRING,
+  })
+  declare facebookId: string;
+  @Column({
+    type: DataType.BIGINT,
+  })
+  declare phoneNum: number;
+  @Column({
+    type: DataType.STRING,
+  })
+  declare password: string | null;
+  @Column({
+    type: DataType.STRING,
+  })
+  declare expoPushToken: string | null;
+ 
+  @Column({
+    type: DataType.BOOLEAN,
+  })
+  declare vip: boolean;
+  @Column({
+    type: DataType.BOOLEAN,
+  })
+  declare verifiedUser: boolean;
+  @ForeignKey(() => Role)
+  @Column({
+    type: DataType.UUID,
+  })
+  roleId!: string;
+ 
+  @BelongsTo(() => Role)
+  role!: Role;
+ 
+  @HasMany(() => Order, "buyerId")
+  ordersAsBuyer?: Order[];
+ 
+  @HasMany(() => Order, "sellerId")
+  ordersAsSeller?: Order[];
+ 
+  @HasMany(() => Product, "userId")
+  product?: Product[];
+ 
+  @HasMany(() => BuyerReview, "userId")
+  buyerReviews?: BuyerReview[];
+ 
+  @CreatedAt
+  declare createdAt: Date;
+  @UpdatedAt
+  declare updatedAt: Date;
+}
+ 
+export default User;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/farming-product-REST-api/src/models/userotpcode.ts.html b/coverage/farming-product-REST-api/src/models/userotpcode.ts.html new file mode 100644 index 0000000..e359586 --- /dev/null +++ b/coverage/farming-product-REST-api/src/models/userotpcode.ts.html @@ -0,0 +1,226 @@ + + + + + + Code coverage report for farming-product-REST-api/src/models/userotpcode.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api/src/models userotpcode.ts

+
+ +
+ 100% + Statements + 37/37 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 100% + Functions + 0/0 +
+ + +
+ 100% + Lines + 37/37 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +481x +  +  +  +  +  +  +  +  +  +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +  +1x + 
import {
+  Table,
+  Column,
+  Model,
+  DataType,
+  CreatedAt,
+  UpdatedAt,
+  ForeignKey,
+} from "sequelize-typescript";
+import { UserOTPAttributes } from "../types";
+import User from "./user";
+@Table({
+  timestamps: true,
+  tableName: "userOTPCodes",
+  modelName: "UserOTPCode",
+})
+class UserOTPCode extends Model<UserOTPAttributes> {
+  @Column({
+    primaryKey: true,
+    type: DataType.UUID,
+    defaultValue: DataType.UUIDV4,
+    allowNull: false,
+  })
+  declare id: string;
+  @Column({
+    type: DataType.STRING,
+  })
+  @ForeignKey(() => User)
+  @Column({
+    type: DataType.UUID,
+  })
+  userId!: string;
+  @Column({
+    type: DataType.STRING,
+  })
+  declare otp: string;
+  @Column({
+    type: DataType.BIGINT,
+  })
+  declare expiredAt: number;
+  @CreatedAt
+  declare createdAt: Date;
+  @UpdatedAt
+  declare updatedAt: Date;
+}
+ 
+export default UserOTPCode;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/farming-product-REST-api/src/routes/auth.routes.ts.html b/coverage/farming-product-REST-api/src/routes/auth.routes.ts.html new file mode 100644 index 0000000..063a88c --- /dev/null +++ b/coverage/farming-product-REST-api/src/routes/auth.routes.ts.html @@ -0,0 +1,1081 @@ + + + + + + Code coverage report for farming-product-REST-api/src/routes/auth.routes.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api/src/routes auth.routes.ts

+
+ +
+ 100% + Statements + 11/11 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 100% + Functions + 0/0 +
+ + +
+ 100% + Lines + 11/11 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +3331x +1x +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +1x + 
import express, { Router } from "express";
+import {
+  verifyPhone,
+  register_user,
+  logIn,
+  verifyOtp,
+  authHandler,
+  sendNewOTP,
+  refreshToken,
+} from "../controllers/auth.controller";
+ 
+const authRouter: Router = express.Router();
+ 
+/**
+ * @swagger
+ * tags:
+ *   name: Authentication
+ *   description: Endpoints for user authentication, registration, and JWT management
+ */
+// signup
+/**
+ * @swagger
+ * /auth/signup:
+ *  post:
+ *    summary: phone verification api
+ *    tags: [Authentication]
+ *    requestBody:
+ *      required: true
+ *      content:
+ *        application/json:
+ *          schema:
+ *            type: object
+ *            properties:
+ *              email:
+ *                type: string
+ *              password:
+ *                type: string
+ *              userRole:
+ *                type: string
+ *              phoneNum:
+ *                type: string
+ *              country:
+ *                type: string
+ *
+ *
+ *    responses:
+ *      200:
+ *        description: The OTP sent successfully
+ *
+ *      500:
+ *        description: Some server error
+ *
+ */
+authRouter.post("/signup", verifyPhone);
+ 
+// verify OTP
+ 
+/**
+ * @swagger
+ * /auth/verifyOTP:
+ *  post:
+ *    summary: phone verification api
+ *    tags: [Authentication]
+ *    requestBody:
+ *      required: true
+ *      content:
+ *        application/json:
+ *          schema:
+ *            type: object
+ *            properties:
+ *              email:
+ *                type: string
+ *              otp:
+ *                type: string
+ *    responses:
+ *      200:
+ *        description: The OTP sent successfully
+ *
+ *      500:
+ *        description: Some server error
+ *
+ */
+authRouter.post("/verifyOTP", verifyOtp);
+ 
+// user registration
+ 
+/**
+ * @swagger
+ * /auth/signup/{userId}:
+ *  put:
+ *    summary: phone verification api
+ *    tags: [Authentication]
+ *    parameters:
+ *      - in: path
+ *        name: userId
+ *        schema:
+ *          type: string
+ *        required: true
+ *    requestBody:
+ *      required: true
+ *      content:
+ *        multipart/form-data:
+ *          schema:
+ *            type: object
+ *            properties:
+ *              firstName:
+ *                type: string
+ *              lastName:
+ *                type: string
+ *              address:
+ *                type: string
+ *              profileImage:
+ *                type: string
+ *                format: binary
+ *    responses:
+ *      200:
+ *        description: The book sent successfully
+ *
+ *      500:
+ *        description: Some server error
+ *
+ */
+authRouter.put("/signup/:userId", register_user);
+ 
+// login
+ 
+/**
+ * @swagger
+ * /auth/login:
+ *   post:
+ *     summary: Log in with email and password
+ *     tags: [Authentication]
+ *     requestBody:
+ *       required: true
+ *       content:
+ *         application/json:
+ *           schema:
+ *             type: object
+ *             properties:
+ *               email:
+ *                 type: string
+ *               password:
+ *                 type: string
+ *     responses:
+ *       200:
+ *         description: OK
+ *         content:
+ *           application/json:
+ *             schema:
+ *               type: object
+ *               properties:
+ *                 message:
+ *                   type: string
+ *                   example: "Authentication Successful"
+ *                 token:
+ *                   type: string
+ *                   description: JWT access token
+ *                 refreshToken:
+ *                   type: string
+ *                   description: JWT refresh token
+ *                 userData:
+ *                   type: object
+ *                   description: User information
+ *       500:
+ *         description: Internal Server Error
+ *         content:
+ *           application/json:
+ *             schema:
+ *               type: object
+ *               properties:
+ *                 error:
+ *                   type: string
+ *                 message:
+ *                   type: string
+ */
+authRouter.post("/login", logIn);
+ 
+// send OTP
+ 
+/**
+ * @swagger
+ * /auth/sendOTP:
+ *  post:
+ *    summary: phone verification api
+ *    tags: [Authentication]
+ *    requestBody:
+ *      required: true
+ *      content:
+ *        application/json:
+ *          schema:
+ *            type: object
+ *            properties:
+ *              email:
+ *                type: string
+ *    responses:
+ *      200:
+ *        description: The OTP sent successfully
+ *
+ *      500:
+ *        description: Some server error
+ *
+ */
+authRouter.post("/sendOTP/", sendNewOTP);
+ 
+// oAuth
+ 
+/**
+ * @swagger
+ * /auth/signup/oAuth:
+ *   post:
+ *     summary: Authenticate using Google or Facebook OAuth
+ *     tags: [Authentication]
+ *     description: Authenticates a user using either a Google token or Facebook token
+ *     requestBody:
+ *       required: true
+ *       content:
+ *         application/json:
+ *           schema:
+ *             type: object
+ *             properties:
+ *               googleToken:
+ *                 type: string
+ *                 description: Google OAuth ID token
+ *               facebookToken:
+ *                 type: string
+ *                 description: Facebook access token
+ *             example:
+ *               googleToken: "eyJhbGciOiJSUzI1NiIsImtpZCI6IjFlOTczZWUyZT..."
+ *     responses:
+ *       200:
+ *         description: User authenticated successfully
+ *         content:
+ *           application/json:
+ *             schema:
+ *               type: object
+ *               properties:
+ *                 message:
+ *                   type: string
+ *                   example: "User logged in successfully"
+ *                 token:
+ *                   type: string
+ *                   description: JWT access token
+ *                   example: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
+ *                 user:
+ *                   type: object
+ *                   properties:
+ *                     id:
+ *                       type: string
+ *                       example: "60d21b4667d0d8992e610c85"
+ *                     email:
+ *                       type: string
+ *                       example: "user@example.com"
+ *                     firstName:
+ *                       type: string
+ *                       example: "John"
+ *                     lastName:
+ *                       type: string
+ *                       example: "Doe"
+ *                     imageUrl:
+ *                       type: string
+ *                       example: "https://lh3.googleusercontent.com/a/user_photo"
+ *                     roleId:
+ *                       type: string
+ *                       example: "60d21b4667d0d8992e610c86"
+ *       400:
+ *         description: Bad request
+ *         content:
+ *           application/json:
+ *             schema:
+ *               type: object
+ *               properties:
+ *                 message:
+ *                   type: string
+ *                   example: "Missing authentication token"
+ *       401:
+ *         description: Unauthorized
+ *         content:
+ *           application/json:
+ *             schema:
+ *               type: object
+ *               properties:
+ *                 message:
+ *                   type: string
+ *                   example: "Invalid Google token"
+ *       500:
+ *         description: Server error
+ *         content:
+ *           application/json:
+ *             schema:
+ *               type: object
+ *               properties:
+ *                 message:
+ *                   type: string
+ *                   example: "User creation failed. Please try again."
+ */
+authRouter.post("/signup/oAuth", authHandler);
+ 
+// refresh token to generate access token
+/**
+ * @swagger
+ * /auth/refreshToken:
+ *   post:
+ *     summary: Refresh access token
+ *     tags: [Authentication]
+ *     security:
+ *       - bearerAuth: []
+ *     description: Generates a new access token using the refresh token provided in the Authorization header
+ *     responses:
+ *       200:
+ *         description: New access token generated successfully
+ *         content:
+ *           application/json:
+ *             schema:
+ *               type: object
+ *               properties:
+ *                 accessToken:
+ *                   type: string
+ *                   description: The new access token
+ *                 message:
+ *                   type: string
+ *                   description: the success message
+ *               example:
+ *                 accessToken: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
+ *                 message: Token refreshed successfully...
+ *       401:
+ *         description: Expired or invalid refresh token
+ *       403:
+ *         description: Forbidden - token has been invalidated
+ */
+authRouter.post("/refreshToken", refreshToken);
+ 
+export default authRouter;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/farming-product-REST-api/src/routes/index.html b/coverage/farming-product-REST-api/src/routes/index.html new file mode 100644 index 0000000..f097e42 --- /dev/null +++ b/coverage/farming-product-REST-api/src/routes/index.html @@ -0,0 +1,221 @@ + + + + + + Code coverage report for farming-product-REST-api/src/routes + + + + + + + + + +
+
+

All files farming-product-REST-api/src/routes

+
+ +
+ 72.39% + Statements + 160/221 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/6 +
+ + +
+ 72.39% + Lines + 160/221 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
auth.routes.ts +
+
100%11/11100%0/0100%0/0100%11/11
index.ts +
+
100%18/18100%0/0100%0/0100%18/18
notification.routes.ts +
+
73.07%19/26100%0/00%0/173.07%19/26
order.routes.ts +
+
71.42%25/35100%0/00%0/171.42%25/35
payment.collection.routes.ts +
+
64.51%20/31100%0/00%0/164.51%20/31
product.routes.ts +
+
73.33%33/45100%0/00%0/173.33%33/45
review.routes.ts +
+
60%15/25100%0/00%0/160%15/25
user.routes.ts +
+
63.33%19/30100%0/00%0/163.33%19/30
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/farming-product-REST-api/src/routes/index.ts.html b/coverage/farming-product-REST-api/src/routes/index.ts.html new file mode 100644 index 0000000..292a0e1 --- /dev/null +++ b/coverage/farming-product-REST-api/src/routes/index.ts.html @@ -0,0 +1,148 @@ + + + + + + Code coverage report for farming-product-REST-api/src/routes/index.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api/src/routes index.ts

+
+ +
+ 100% + Statements + 18/18 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 100% + Functions + 0/0 +
+ + +
+ 100% + Lines + 18/18 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +221x +1x +1x +1x +1x +1x +1x +1x +  +1x +  +1x +1x +1x +1x +1x +1x +1x +1x +  +1x + 
import express, { Router } from "express";
+import authRoutes from "./auth.routes";
+import userRoutes from "./user.routes";
+import productRouter from "./product.routes";
+import reviewRouter from "./review.routes";
+import orderRouter from "./order.routes";
+import paymentCollectionRoute from "./payment.collection.routes";
+import notificationRouter from "./notification.routes";
+ 
+const appRouter: Router = express.Router();
+ 
+console.log("\n\n from route index file: \n\n");
+appRouter.use("/auth", authRoutes);
+appRouter.use("/users", userRoutes);
+appRouter.use("/products", productRouter);
+appRouter.use("/reviews", reviewRouter);
+appRouter.use("/orders", orderRouter);
+appRouter.use("/transactions", paymentCollectionRoute);
+appRouter.use("/notifications", notificationRouter);
+ 
+export default appRouter;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/farming-product-REST-api/src/routes/notification.routes.ts.html b/coverage/farming-product-REST-api/src/routes/notification.routes.ts.html new file mode 100644 index 0000000..972c841 --- /dev/null +++ b/coverage/farming-product-REST-api/src/routes/notification.routes.ts.html @@ -0,0 +1,862 @@ + + + + + + Code coverage report for farming-product-REST-api/src/routes/notification.routes.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api/src/routes notification.routes.ts

+
+ +
+ 73.07% + Statements + 19/26 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 73.07% + Lines + 19/26 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +2601x +1x +1x +  +1x +  +  +  +  +  +  +1x +1x +1x +1x +  +  +  +  +  +  +  +  +  +1x +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +1x +1x +1x +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +1x + 
import express, { NextFunction, Request, Response, Router } from "express";
+import authCheck from "../middleware/auth-check";
+import { z } from "zod";
+ 
+import {
+  createNotification,
+  getNotification,
+  markAsRead,
+  testExpoNotification,
+} from "../controllers/notification.controller";
+ 
+const notificationSchema = z.object({
+  title: z.string().min(1),
+  message: z.string().min(1),
+});
+ 
+function validateNotification(req: Request, res: Response, next: NextFunction) {
+  const parsed = notificationSchema.safeParse(req.body);
+  if (!parsed.success) {
+    return res.status(400).json({ errors: parsed.error.flatten().fieldErrors });
+  }
+  next();
+}
+ 
+const notificationRouter: Router = express.Router();
+ 
+notificationRouter.use(authCheck);
+/**
+ * @swagger
+ * components:
+ *   schemas:
+ *     Notification:
+ *       type: object
+ *       required:
+ *         - userId
+ *         - title
+ *         - message
+ *       properties:
+ *         id:
+ *           type: string
+ *           description: The auto-generated id of the notification
+ *         userId:
+ *           type: string
+ *           description: The user ID this notification belongs to
+ *         title:
+ *           type: string
+ *           description: The title of the notification
+ *         message:
+ *           type: string
+ *           description: The content of the notification
+ *         isRead:
+ *           type: boolean
+ *           description: Indicates if the notification has been read
+ *         createdAt:
+ *           type: string
+ *           format: date-time
+ *           description: The date the notification was created
+ *       example:
+ *         id: 60d21b4667d0d8992e610c85
+ *         userId: 60d21b4667d0d8992e610c86
+ *         title: New Order
+ *         message: Your order has been received
+ *         isRead: false
+ *         createdAt: 2023-01-10T04:05:06.157Z
+ */
+ 
+/**
+ * @swagger
+ * tags:
+ *   name: Notifications
+ *   description: API for managing user notifications
+ */
+ 
+/**
+ * @swagger
+ * /notifications/create/{userId}:
+ *   post:
+ *     summary: Create a new notification for a user
+ *     tags: [Notifications]
+ *     parameters:
+ *       - in: path
+ *         name: userId
+ *         schema:
+ *           type: string
+ *         required: true
+ *         description: The user id
+ *     requestBody:
+ *       required: true
+ *       content:
+ *         application/json:
+ *           schema:
+ *             type: object
+ *             required:
+ *               - title
+ *               - message
+ *             properties:
+ *               title:
+ *                 type: string
+ *                 description: Notification title
+ *               message:
+ *                 type: string
+ *                 description: Notification message
+ *             example:
+ *               title: "Order Update"
+ *               message: "Your order has been shipped"
+ *     responses:
+ *       200:
+ *         description: Notification created successfully
+ *         content:
+ *           application/json:
+ *             schema:
+ *               type: object
+ *               properties:
+ *                 result:
+ *                   $ref: '#/components/schemas/Notification'
+ *       500:
+ *         description: Server error
+ */
+notificationRouter.post(
+  "/create/:userId",
+  validateNotification,
+  createNotification,
+);
+ 
+/**
+ * @swagger
+ * /notifications/{userId}:
+ *   get:
+ *     summary: Get all notifications for a user
+ *     tags: [Notifications]
+ *     parameters:
+ *       - in: path
+ *         name: userId
+ *         schema:
+ *           type: string
+ *         required: true
+ *         description: The user id
+ *     responses:
+ *       200:
+ *         description: List of notifications for the user
+ *         content:
+ *           application/json:
+ *             schema:
+ *               type: object
+ *               properties:
+ *                 notifiations:
+ *                   type: object
+ *                   properties:
+ *                     count:
+ *                       type: integer
+ *                       example: 2
+ *                     rows:
+ *                       type: array
+ *                       items:
+ *                         type: object
+ *                         properties:
+ *                           id:
+ *                             type: string
+ *                             example: "60d21b4667d0d8992e610c85"
+ *                           userId:
+ *                             type: string
+ *                             example: "60d21b4667d0d8992e610c86"
+ *                           title:
+ *                             type: string
+ *                             example: "New Order"
+ *                           message:
+ *                             type: string
+ *                             example: "Your order has been received"
+ *                           isRead:
+ *                             type: boolean
+ *                             example: false
+ *                           createdAt:
+ *                             type: string
+ *                             format: date-time
+ *                             example: "2023-01-10T04:05:06.157Z"
+ *                           User:
+ *                             type: object
+ *                             properties:
+ *                               id:
+ *                                 type: string
+ *                                 example: "60d21b4667d0d8992e610c86"
+ *                               firstName:
+ *                                 type: string
+ *                                 example: "John"
+ *                               lastName:
+ *                                 type: string
+ *                                 example: "Doe"
+ *                               country:
+ *                                 type: string
+ *                                 example: "Cameroon"
+ *                               verifiedUser:
+ *                                 type: boolean
+ *                                 example: true
+ *       500:
+ *         description: Server error
+ */
+notificationRouter.get("/:userId", getNotification);
+ 
+/**
+ * @swagger
+ * /notifications/{id}:
+ *   put:
+ *     summary: Mark a notification as read
+ *     tags: [Notifications]
+ *     parameters:
+ *       - in: path
+ *         name: id
+ *         schema:
+ *           type: string
+ *         required: true
+ *         description: The notification id
+ *     responses:
+ *       200:
+ *         description: Notification marked as read
+ *         content:
+ *           application/json:
+ *             schema:
+ *               type: object
+ *               properties:
+ *                 message:
+ *                   type: string
+ *                   example: "success"
+ *       500:
+ *         description: Server error
+ */
+notificationRouter.put("/:id", markAsRead);
+ 
+// test expo notification
+ 
+/**
+ * @swagger
+ * /notifications/{userId}/test:
+ *   get:
+ *     summary: Send a test push notification to user's device
+ *     tags: [Notifications]
+ *     parameters:
+ *       - in: path
+ *         name: userId
+ *         schema:
+ *           type: string
+ *         required: true
+ *         description: The user id
+ *     responses:
+ *       200:
+ *         description: Test notification sent
+ *         content:
+ *           application/json:
+ *             schema:
+ *               type: object
+ *               properties:
+ *                 message:
+ *                   type: object
+ *                   description: Response from Expo push notification service
+ *       500:
+ *         description: Server error
+ */
+notificationRouter.get("/:userId/test", testExpoNotification);
+ 
+export default notificationRouter;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/farming-product-REST-api/src/routes/order.routes.ts.html b/coverage/farming-product-REST-api/src/routes/order.routes.ts.html new file mode 100644 index 0000000..7f8c0d9 --- /dev/null +++ b/coverage/farming-product-REST-api/src/routes/order.routes.ts.html @@ -0,0 +1,883 @@ + + + + + + Code coverage report for farming-product-REST-api/src/routes/order.routes.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api/src/routes order.routes.ts

+
+ +
+ 71.42% + Statements + 25/35 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 71.42% + Lines + 25/35 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +2671x +1x +1x +  +  +  +  +  +  +  +  +1x +1x +  +1x +1x +1x +1x +1x +1x +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +1x +1x +1x +1x +1x +  +1x +  +1x + 
import express, { NextFunction, Request, Response, Router } from "express";
+import authCheck from "../middleware/auth-check";
+import {
+  getOrderById,
+  createOrder,
+  updateOrder,
+  getBuyerOrders,
+  getSellerOrders,
+  getTransaction,
+  updateDispatchDetails,
+} from "../controllers/order.controller";
+import uploadMiddleware from "../middleware/multerStorage";
+import { z } from "zod";
+ 
+const orderSchema = z.object({
+  shipAddress: z.string().min(1),
+  weight: z.string().min(1),
+  sellerId: z.string().uuid(),
+  amount: z.number().min(1),
+});
+ 
+function validateOrder(req: Request, res: Response, next: NextFunction) {
+  const parsed = orderSchema.safeParse({
+    ...req.body,
+    amount: req.body.amount ? Number(req.body.amount) : undefined,
+  });
+  if (!parsed.success) {
+    return res.status(400).json({ errors: parsed.error.flatten().fieldErrors });
+  }
+  next();
+}
+ 
+const orderRouter: Router = express.Router();
+ 
+orderRouter.use(authCheck);
+/**
+ * @swagger
+ * components:
+ *   schemas:
+ *     order:
+ *       type: object
+ *       required:
+ *         - userId
+ *         - title
+ *         - message
+ *       properties:
+ *         id:
+ *           type: string
+ *           description: The auto-generated id of the order
+ *         buyerId:
+ *           type: string
+ *           description: The user ID for the buyer of this order belongs to
+ *         sellerId:
+ *           type: string
+ *           description: The user ID for the seller of this order belongs to
+ *         amoount:
+ *           type: string
+ *           description: The amount of the order
+ *         weight:
+ *           type: string
+ *           description: The weight of the order
+ *         review:
+ *           type: object
+ *           description: Indicates the review made for the order.
+ *         status:
+ *           type: string
+ *           description: The status of the order
+ *         dispatchDetails:
+ *           type: any
+ *           description: The dispatch details of the order
+ *         dispatched:
+ *           type: boolean
+ *           description: Indicates if the order has been dispatched
+ *         deliveryDate:
+ *           type: Date
+ *           description: The delivery date of the order
+ *         prodId:
+ *           type: string
+ *           description: The product ID of the order
+ *         createdAt:
+ *           type: string
+ *           format: date-time
+ *           description: The date the order was created
+ *         updatedAt:
+ *           type: string
+ *           format: date-time
+ *           description: The date the order was updated
+ *       example:
+ *         id: 60d21b4667d0d8992e610c85
+ *         userId: 60d21b4667d0d8992e610c86
+ *         title: New Order
+ *         message: Your order has been received
+ *         isRead: false
+ *         createdAt: 2023-01-10T04:05:06.157Z
+ */
+ 
+/**
+ * @swagger
+ * tags:
+ *   name: Orders
+ *   description: API for managing user orders
+ */
+ 
+/**
+ * @swagger
+ * /user/order/buyer/{buyerId}:
+ *  get:
+ *      summary: all order of a buyer
+ *      tags: [Orders]
+ *      security:
+ *          - bearerAuth: []
+ *      parameters:
+ *        - in: path
+ *          name: buyerId
+ *          schema:
+ *              type: string
+ *          required: true
+ *        - in: query
+ *          name: orderStatus
+ *          schema:
+ *              type: string
+ *          required: false
+ *      responses:
+ *          200:
+ *              description: Ok. a product Data based on the product ID
+ *          401:
+ *              description: Forbiden or unauthorized
+ *          500:
+ *              description: Server Error
+ */
+// get all orders of the buyer with query parameter of status
+orderRouter.get("/buyer/:buyerId", getBuyerOrders);
+ 
+/**
+ * @swagger
+ * /user/order/seller/{sellerId}:
+ *  get:
+ *      summary: all order of a seller
+ *      tags: [Orders]
+ *      security:
+ *          - bearerAuth: []
+ *      parameters:
+ *        - in: path
+ *          name: sellerId
+ *          schema:
+ *              type: string
+ *          required: true
+ *        - in: query
+ *          name: orderStatus
+ *          schema:
+ *              type: string
+ *          required: false
+ *      responses:
+ *          200:
+ *              description: Ok. a product Data based on the product ID
+ *          401:
+ *              description: Forbiden or unauthorized
+ *          500:
+ *              description: Server Error
+ */
+ 
+// get all orders of the seller with query parameter of status
+orderRouter.get("/seller/:sellerId", getSellerOrders);
+ 
+/**
+ * @swagger
+ * /user/order/{orderId}:
+ *  get:
+ *      summary: fetching an order using ID
+ *      tags: [Orders]
+ *      security:
+ *          - bearerAuth: []
+ *      parameters:
+ *        - in: path
+ *          name: orderId
+ *          schema:
+ *              type: string
+ *          required: true
+ *      responses:
+ *          200:
+ *              description: Ok. a product Data based on the product ID
+ *          401:
+ *              description: Forbiden or unauthorized
+ *          500:
+ *              description: Server Error
+ */
+ 
+orderRouter.get("/:orderId", getOrderById);
+ 
+/**
+ * @swagger
+ * /user/order/new/{productId}:
+ *  post:
+ *      summary: Place an order to the product
+ *      tags: [Orders]
+ *      security:
+ *          - bearerAuth: []
+ *      parameters:
+ *        - in: path
+ *          name: productId
+ *          schema:
+ *              type: string
+ *          required: true
+ *      requestBody:
+ *          required: true
+ *          content:
+ *              application/json:
+ *                  schema:
+ *                      type: object
+ *                      properties:
+ *                          shipAddress:
+ *                              type: string
+ *                          weight:
+ *                              type: string
+ *                          sellerId:
+ *                              type: string
+ *                          amount:
+ *                              type: integer
+ *
+ *      responses:
+ *          200:
+ *              description: OK
+ *          403:
+ *              description: Forbiden
+ *          500:
+ *              description: Internal Server Error
+ *          429:
+ *              description: Too many requests
+ */
+orderRouter.post("/new/:productId", validateOrder, createOrder);
+ 
+/**
+ * @swagger
+ * /user/order/{orderId}/complete:
+ *  put:
+ *      summary: Make the order status as complete
+ *      tags: [Orders]
+ *      security:
+ *          - bearerAuth: []
+ *      parameters:
+ *        - in: path
+ *          name: orderId
+ *          schema:
+ *              type: string
+ *          required: true
+ *      responses:
+ *          200:
+ *              description: OK
+ *          403:
+ *              description: Forbiden
+ *          500:
+ *              description: Internal Server Error
+ *          429:
+ *              description: Too many requests
+ */
+orderRouter.put("/:orderId/complete", updateOrder);
+ 
+orderRouter.put(
+  "/:orderId/dispatch",
+  uploadMiddleware("dispatchImage"),
+  updateDispatchDetails,
+);
+ 
+orderRouter.get("/:orderId/transaction", getTransaction);
+ 
+export default orderRouter;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/farming-product-REST-api/src/routes/payment.collection.routes.ts.html b/coverage/farming-product-REST-api/src/routes/payment.collection.routes.ts.html new file mode 100644 index 0000000..ba92313 --- /dev/null +++ b/coverage/farming-product-REST-api/src/routes/payment.collection.routes.ts.html @@ -0,0 +1,610 @@ + + + + + + Code coverage report for farming-product-REST-api/src/routes/payment.collection.routes.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api/src/routes payment.collection.routes.ts

+
+ +
+ 64.51% + Statements + 20/31 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 64.51% + Lines + 20/31 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +1761x +1x +1x +  +  +  +1x +  +1x +1x +1x +1x +1x +1x +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +1x +1x +1x +1x +1x +  +1x + 
import express, { NextFunction, Request, Response, Router } from "express";
+import authCheck from "../middleware/auth-check";
+import {
+  collectionResponseAdwa,
+  mobilePaymentCollection,
+} from "../controllers/payment.collection.controller";
+import { z } from "zod";
+ 
+const paymentSchema = z.object({
+  meanCode: z.string().min(1),
+  paymentNumber: z.string().min(1),
+  currency: z.string().min(1),
+  feesAmount: z.number().min(0),
+  amount: z.number().min(1),
+});
+ 
+function validatePayment(req: Request, res: Response, next: NextFunction) {
+  const parsed = paymentSchema.safeParse({
+    ...req.body,
+    amount: req.body.amount ? Number(req.body.amount) : undefined,
+    feesAmount: req.body.feesAmount ? Number(req.body.feesAmount) : undefined,
+  });
+  if (!parsed.success) {
+    return res.status(400).json({ errors: parsed.error.flatten().fieldErrors });
+  }
+  next();
+}
+ 
+const paymentCollectionRoute: Router = express.Router();
+ 
+paymentCollectionRoute.post("/webhook/adwapay", collectionResponseAdwa);
+ 
+/**
+ * @swagger
+ * components:
+ *  securitySchemes:
+ *      bearerAuth:
+ *          type: http
+ *          scheme: bearer
+ *          bearerFormat: JWT
+ *  security:
+ *      - bearerAuth: []
+ */
+ 
+/**
+ * @swagger
+ * tags:
+ *   name: Payment Collection
+ *   description: API for managing user payment Collection
+ */
+ 
+/**
+ * @swagger
+ * components:
+ *   schemas:
+ *     PaymentRequest:
+ *       type: object
+ *       required:
+ *         - amount
+ *         - meanCode
+ *         - currency
+ *       properties:
+ *         amount:
+ *           type: string
+ *           description: The payment amount
+ *           example: "1000.00"
+ *         meanCode:
+ *           type: string
+ *           description: Payment method code (MASTERCARD, VISA, etc.)
+ *           example: "MASTERCARD"
+ *         currency:
+ *           type: string
+ *           description: Currency code
+ *           example: "XAF"
+ *         phoneNumber:
+ *           type: string
+ *           description: Customer phone number (required for mobile payments)
+ *           example: "237612345678"
+ *     PaymentResponse:
+ *       type: object
+ *       properties:
+ *         status:
+ *           type: string
+ *           description: Status of the payment operation
+ *           example: "success"
+ *         message:
+ *           type: object
+ *           description: Payment processing response
+ *           properties:
+ *             status:
+ *               type: string
+ *               example: "T"
+ *             transactionId:
+ *               type: string
+ *               example: "PAY123456789"
+ *             timestamp:
+ *               type: string
+ *               example: "2023-03-15T10:20:30Z"
+ */
+ 
+/**
+ * @swagger
+ * /user/transaction/{orderId}/paymentCollection/mobile:
+ *  post:
+ *      summary: Payment route for either mobile or card
+ *      tags: [Payment Collection]
+ *      security:
+ *          - bearerAuth: []
+ *      parameters:
+ *        - in: path
+ *          name: orderId
+ *          schema:
+ *              type: string
+ *          required: true
+ *      requestBody:
+ *          required: true
+ *          content:
+ *              application/json:
+ *                  schema:
+ *                      type: object
+ *                      properties:
+ *                          meanCode:
+ *                              type: string
+ *                          paymentNumber:
+ *                              type: string
+ *                          currency:
+ *                              type: string
+ *                          feesAmount:
+ *                              type: string
+ *                          amount:
+ *                              type: integer
+ *                      example:
+ *                          meanCode: MOBILE-MONEY or ORANGE-MONEY or VISA or MASTERCARD
+ *                          paymentNumber: 672151908
+ *                          currency: XAF
+ *                          amount: 200
+ *                          feesAmount: 0
+ *
+ *      responses:
+ *          200:
+ *              description: data object in the response contains the following example output which you may use. For visa or mastercard redirect the user the card pay link where the user will complete the transaction
+ *              content:
+ *                  application/json:
+ *                      schema:
+ *                          type: object
+ *                      examples:
+ *                          MOBILE-MONEY OR ORANGE-MONEY:
+ *                              value:
+ *                                  adpFootprint: GOSHENWATER_CDF412FBA4535
+ *                                  status: T
+ *                                  meanCode: MOBILE-MONEY,
+ *                                  orderNumber: order_a049662d-e152-4dae-8e30-c010cc95435e_1718824055475
+ *                          VISA or MASTERCARD:
+ *                              value:
+ *                                  adpFootprint: GOSHENWATER_56964554A5E4C
+ *                                  orderNumber: order_a049662d-e152-4dae-8e30-c010cc95435e_1718865202762
+ *                                  status: E
+ *                                  description: null
+ *                                  CARD_PAY_LINK: https://dev.adwa.world/ADPFO/BANK/ADWACARD?adpFootprint=GOSHENWATER_56964554A5E4C
+ *          403:
+ *              description: Forbiden
+ *          500:
+ *              description: Internal Server Error
+ *          429:
+ *              description: Too many requests
+ */
+ 
+paymentCollectionRoute.post(
+  "/:orderId/paymentCollection/mobile",
+  authCheck,
+  validatePayment,
+  mobilePaymentCollection,
+);
+ 
+export default paymentCollectionRoute;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/farming-product-REST-api/src/routes/product.routes.ts.html b/coverage/farming-product-REST-api/src/routes/product.routes.ts.html new file mode 100644 index 0000000..c1e574a --- /dev/null +++ b/coverage/farming-product-REST-api/src/routes/product.routes.ts.html @@ -0,0 +1,1183 @@ + + + + + + Code coverage report for farming-product-REST-api/src/routes/product.routes.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api/src/routes product.routes.ts

+
+ +
+ 73.33% + Statements + 33/45 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 73.33% + Lines + 33/45 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +335 +336 +337 +338 +339 +340 +341 +342 +343 +344 +345 +346 +347 +348 +349 +350 +351 +352 +353 +354 +355 +356 +357 +358 +359 +360 +361 +362 +363 +364 +365 +366 +3671x +1x +1x +1x +  +  +  +  +  +  +1x +1x +  +1x +1x +1x +1x +1x +1x +1x +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +1x +1x +1x +1x +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +1x +1x +1x +1x +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +1x + 
import express, { NextFunction, Request, Response, Router } from "express";
+import authCheck from "../middleware/auth-check";
+import uploadMiddleware from "../middleware/multerStorage";
+import {
+  getProduct,
+  createProduct,
+  updateProduct,
+  removeProduct,
+  userProducts,
+} from "../controllers/product.controller";
+import { getAllProductSearch } from "../controllers/product.search.controller";
+import { z } from "zod";
+ 
+const productSchema = z.object({
+  productName: z.string().min(1),
+  productCat: z.string().min(1),
+  priceType: z.string().min(1),
+  price: z.number().min(0),
+  description: z.string().optional(),
+  wholeSale: z.boolean().optional(),
+});
+ 
+function validateProduct(req: Request, res: Response, next: NextFunction) {
+  const data = req.body;
+  // If using multer, req.body fields may be strings, so coerce as needed
+  const parsed = productSchema.safeParse({
+    ...data,
+    price: data.price ? Number(data.price) : undefined,
+    wholeSale: data.wholeSale === "true" || data.wholeSale === true,
+  });
+  if (!parsed.success) {
+    return res.status(400).json({ errors: parsed.error.flatten().fieldErrors });
+  }
+  next();
+}
+ 
+const productRouter: Router = express.Router();
+ 
+productRouter.use(authCheck);
+ 
+/**
+ * @swagger
+ * components:
+ *  securitySchemes:
+ *      bearerAuth:
+ *          type: http
+ *          scheme: bearer
+ *          bearerFormat: JWT
+ *  security:
+ *      - bearerAuth: []
+ */
+ 
+/**
+ * @swagger
+ * tags:
+ *   name: Products
+ *   description: API for managing user products
+ */
+ 
+/**
+ * @swagger
+ * components:
+ *   schemas:
+ *     Product:
+ *       type: object
+ *       required:
+ *         - userId
+ *       properties:
+ *         id:
+ *           type: string
+ *           format: uuid
+ *           description: The auto-generated id of the product
+ *           example: 550e8400-e29b-41d4-a716-446655440000
+ *         productName:
+ *           type: string
+ *           description: Name of the product
+ *           example: Organic Fresh Tomatoes
+ *         productCat:
+ *           type: string
+ *           description: Category of the product
+ *           example: Vegetables
+ *         priceType:
+ *           type: string
+ *           description: Type of pricing (e.g., per kg, per piece)
+ *           example: per kg
+ *         price:
+ *           type: number
+ *           format: float
+ *           description: Price of the product
+ *           example: 1500
+ *         imageUrl:
+ *           type: string
+ *           description: URL to the product image
+ *           example: https://farmingproducts-storage.com/images/tomatoes.jpg
+ *         description:
+ *           type: string
+ *           description: Detailed description of the product
+ *           example: Fresh, locally grown organic tomatoes from our partner farms
+ *         wholeSale:
+ *           type: boolean
+ *           description: Indicates if the product is available for wholesale
+ *           example: false
+ *         userId:
+ *           type: string
+ *           format: uuid
+ *           description: ID of the user who created/owns this product
+ *           example: 550e8400-e29b-41d4-a716-446655440001
+ *         createdAt:
+ *           type: string
+ *           format: date-time
+ *           description: The date the product was created
+ *           example: 2023-03-15T12:00:00Z
+ *         updatedAt:
+ *           type: string
+ *           format: date-time
+ *           description: The date the product was last updated
+ *           example: 2023-03-16T14:30:00Z
+ */
+ 
+/**
+ * @swagger
+ * /user/product:
+ *  get:
+ *      summary: Search product using query parameters
+ *      tags: [Products]
+ *      security:
+ *          - bearerAuth: []
+ *      parameters:
+ *        - in: query
+ *          name: productName
+ *          schema:
+ *              type: string
+ *          required: false
+ *        - in: query
+ *          name: productCat
+ *          schema:
+ *              type: string
+ *          required: false
+ *        - in: query
+ *          name: page
+ *          schema:
+ *              type: integer
+ *          required: false
+ *        - in: query
+ *          name: limit
+ *          schema:
+ *              type: integer
+ *          required: false
+ *        - in: query
+ *          name: minPrice
+ *          schema:
+ *              type: integer
+ *          required: false
+ *        - in: query
+ *          name: maxPrice
+ *          schema:
+ *              type: integer
+ *          required: false
+ *      responses:
+ *          200:
+ *              description: Success. getting all products or products on the basis of search query
+ *          500:
+ *              description: Server Error
+ *          403:
+ *              description: Forbidden
+ *
+ */
+ 
+productRouter.get("/", getAllProductSearch);
+ 
+/**
+ * @swagger
+ * /user/product/{productId}:
+ *  get:
+ *      summary: getting a product based on its ID
+ *      tags: [Products]
+ *      security:
+ *          - bearerAuth: []
+ *      parameters:
+ *        - in: path
+ *          name: productId
+ *          schema:
+ *              type: string
+ *          required: true
+ *      responses:
+ *          200:
+ *              description: Ok. a product Data based on the product ID
+ *          403:
+ *              description: Forbiden
+ *          500:
+ *              description: Server Error
+ */
+productRouter.get("/:productId", getProduct);
+ 
+/**
+ * @swagger
+ * /user/product/{userId}/products:
+ *  get:
+ *      summary: all products of the user
+ *      tags: [Products]
+ *      security:
+ *          - bearerAuth: []
+ *      parameters:
+ *        - in: path
+ *          name: userId
+ *          schema:
+ *              type: string
+ *          required: true
+ *      responses:
+ *          200:
+ *              description: OK. products of a user
+ *          500:
+ *              description: Server Error
+ *          403:
+ *              description: Forbiden
+ *          429:
+ *              description: Too many Requests
+ */
+productRouter.get("/:userId/products", userProducts);
+ 
+/**
+ * @swagger
+ * /user/product/add:
+ *   post:
+ *     summary: Add a new product (with image upload)
+ *     tags: [Products]
+ *     security:
+ *       - bearerAuth: []
+ *     requestBody:
+ *       required: true
+ *       content:
+ *         multipart/form-data:
+ *           schema:
+ *             type: object
+ *             required:
+ *               - productName
+ *               - productCat
+ *               - priceType
+ *               - price
+ *               - productImage
+ *             properties:
+ *               productName:
+ *                 type: string
+ *               productCat:
+ *                 type: string
+ *               priceType:
+ *                 type: string
+ *               price:
+ *                 type: number
+ *               description:
+ *                 type: string
+ *               wholeSale:
+ *                 type: boolean
+ *               productImage:
+ *                 type: string
+ *                 format: binary
+ *     responses:
+ *       200:
+ *         description: Product created successfully
+ *       400:
+ *         description: Validation error
+ *         content:
+ *           application/json:
+ *             schema:
+ *               type: object
+ *               properties:
+ *                 errors:
+ *                   type: object
+ *                   example: { productName: ["Required"], price: ["Must be a number"] }
+ *       500:
+ *         description: Internal Server Error
+ */
+ 
+productRouter.post(
+  "/add",
+  uploadMiddleware("productImage"),
+  validateProduct,
+  createProduct,
+);
+ 
+/**
+ * @swagger
+ * /user/product/update/{productId}:
+ *   put:
+ *     summary: Update product fields (with image upload)
+ *     tags: [Products]
+ *     security:
+ *       - bearerAuth: []
+ *     parameters:
+ *       - in: path
+ *         name: productId
+ *         schema:
+ *           type: string
+ *         required: true
+ *     requestBody:
+ *       required: true
+ *       content:
+ *         multipart/form-data:
+ *           schema:
+ *             type: object
+ *             properties:
+ *               productName:
+ *                 type: string
+ *               productCat:
+ *                 type: string
+ *               priceType:
+ *                 type: string
+ *               price:
+ *                 type: number
+ *               description:
+ *                 type: string
+ *               wholeSale:
+ *                 type: boolean
+ *               productImage:
+ *                 type: string
+ *                 format: binary
+ *     responses:
+ *       200:
+ *         description: Product updated successfully
+ *       400:
+ *         description: Validation error
+ *         content:
+ *           application/json:
+ *             schema:
+ *               type: object
+ *               properties:
+ *                 errors:
+ *                   type: object
+ *                   example: { productName: ["Required"], price: ["Must be a number"] }
+ *       500:
+ *         description: Internal Server Error
+ */
+productRouter.put(
+  "/:productId",
+  uploadMiddleware("productImage"),
+  validateProduct,
+  updateProduct,
+);
+ 
+/**
+ * @swagger
+ * /user/product/remove/{productId}:
+ *  delete:
+ *      summary: Caution! removing a product by the user
+ *      tags: [Products]
+ *      security:
+ *          - bearerAuth: []
+ *      parameters:
+ *        - in: path
+ *          name: productId
+ *          required: true
+ *          schema:
+ *              type: string
+ *      responses:
+ *          200:
+ *              description: OK
+ *          403:
+ *              description: Forbiden
+ *          500:
+ *              description: Internal Server Error
+ *          429:
+ *              description: Too many requests
+ */
+productRouter.delete("/:productId", removeProduct);
+ 
+export default productRouter;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/farming-product-REST-api/src/routes/review.routes.ts.html b/coverage/farming-product-REST-api/src/routes/review.routes.ts.html new file mode 100644 index 0000000..a6009c8 --- /dev/null +++ b/coverage/farming-product-REST-api/src/routes/review.routes.ts.html @@ -0,0 +1,910 @@ + + + + + + Code coverage report for farming-product-REST-api/src/routes/review.routes.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api/src/routes review.routes.ts

+
+ +
+ 60% + Statements + 15/25 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 60% + Lines + 15/25 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +2761x +1x +1x +  +  +  +  +  +  +1x +  +1x +1x +1x +1x +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +1x + 
import express, { NextFunction, Request, Response, Router } from "express";
+import authCheck from "../middleware/auth-check";
+import {
+  getReviewByProdId,
+  createReview,
+  updateReview,
+  deleteOwnReview,
+  orderReview,
+} from "../controllers/review.controller";
+import { z } from "zod";
+ 
+const reviewSchema = z.object({
+  comment: z.string().min(1),
+  rating: z.number().min(1).max(5),
+});
+ 
+function validateReview(req: Request, res: Response, next: NextFunction) {
+  const parsed = reviewSchema.safeParse({
+    ...req.body,
+    rating: req.body.rating ? Number(req.body.rating) : undefined,
+  });
+  if (!parsed.success) {
+    return res.status(400).json({ errors: parsed.error.flatten().fieldErrors });
+  }
+  next();
+}
+ 
+const reviewRouter: Router = express.Router();
+ 
+// reviewRouter.get('/:userId')
+ 
+/**
+ * @swagger
+ * components:
+ *  securitySchemes:
+ *      bearerAuth:
+ *          type: http
+ *          scheme: bearer
+ *          bearerFormat: JWT
+ *  security:
+ *      - bearerAuth: []
+ */
+ 
+/**
+ * @swagger
+ * tags:
+ *   name: Reviews
+ *   description: API for managing user reviews
+ */
+ 
+/**
+ * @swagger
+ * components:
+ *   schemas:
+ *     BuyerReview:
+ *       type: object
+ *       required:
+ *         - comment
+ *         - rating
+ *         - userId
+ *         - prodId
+ *         - orderId
+ *       properties:
+ *         id:
+ *           type: string
+ *           format: uuid
+ *           description: The auto-generated id of the review
+ *           example: 550e8400-e29b-41d4-a716-446655440000
+ *         comment:
+ *           type: string
+ *           description: The review comment text
+ *           example: The product was fresh and delivered on time. Excellent quality!
+ *         rating:
+ *           type: number
+ *           minimum: 1
+ *           maximum: 5
+ *           description: Rating given by the buyer (typically 1-5)
+ *           example: 4.5
+ *         userId:
+ *           type: string
+ *           format: uuid
+ *           description: ID of the user who created this review
+ *           example: 550e8400-e29b-41d4-a716-446655440001
+ *         prodId:
+ *           type: string
+ *           format: uuid
+ *           description: ID of the product being reviewed
+ *           example: 550e8400-e29b-41d4-a716-446655440002
+ *         orderId:
+ *           type: string
+ *           format: uuid
+ *           description: ID of the order associated with this review
+ *           example: 550e8400-e29b-41d4-a716-446655440003
+ *         createdAt:
+ *           type: string
+ *           format: date-time
+ *           description: The date the review was created
+ *           example: 2023-03-15T12:00:00Z
+ *         updatedAt:
+ *           type: string
+ *           format: date-time
+ *           description: The date the review was last updated
+ *           example: 2023-03-16T14:30:00Z
+ */
+ 
+/**
+ * @swagger
+ * /user/review/{orderId}:
+ *  get:
+ *      summary: get a review of an order
+ *      tags: [Reviews]
+ *      security:
+ *          - bearerAuth: []
+ *      parameters:
+ *        - in: path
+ *          name: orderId
+ *          schema:
+ *              type: string
+ *          required: true
+ *      responses:
+ *          200:
+ *              description: OK
+ *          403:
+ *              description: Forbiden
+ *          500:
+ *              description: Internal Server Error
+ *          429:
+ *              description: Too many requests
+ */
+ 
+// get review of an order
+reviewRouter.get("/:orderId", authCheck, orderReview);
+ 
+/**
+ * @swagger
+ * /user/review/{productId}:
+ *  get:
+ *      summary: All reviews of a product
+ *      tags: [Reviews]
+ *      security:
+ *          - bearerAuth: []
+ *      parameters:
+ *        - in: path
+ *          name: productId
+ *          schema:
+ *              type: string
+ *          required: true
+ *        - in: query
+ *          name: rating
+ *          schema:
+ *              type: string
+ *          required: false
+ *      responses:
+ *          200:
+ *              description: OK
+ *          403:
+ *              description: Forbiden
+ *          500:
+ *              description: Internal Server Error
+ *          429:
+ *              description: Too many requests
+ */
+ 
+reviewRouter.get("/:productId", authCheck, getReviewByProdId);
+ 
+/**
+ * @swagger
+ * /user/review/{productId}/${orderId}/add:
+ *  post:
+ *      summary: Add review to a product
+ *      tags: [Reviews]
+ *      security:
+ *          - bearerAuth: []
+ *      parameters:
+ *        - in: path
+ *          name: productId
+ *          schema:
+ *              type: string
+ *          required: true
+ *        - in: path
+ *          name: orderId
+ *          schema:
+ *              type: string
+ *          required: true
+ *      requestBody:
+ *          required: true
+ *          content:
+ *              application/json:
+ *                  schema:
+ *                      type: object
+ *                      properties:
+ *                          comment:
+ *                              type: string
+ *                          rating:
+ *                              type: integer
+ *
+ *      responses:
+ *          200:
+ *              description: OK
+ *          403:
+ *              description: Forbiden
+ *          500:
+ *              description: Internal Server Error
+ *          429:
+ *              description: Too many requests
+ */
+ 
+reviewRouter.post("/:productId/:orderId/add", validateReview, createReview);
+ 
+/**
+ * @swagger
+ * /user/review/{reviewId}/update:
+ *  put:
+ *      summary: Update review to a product
+ *      tags: [Reviews]
+ *      security:
+ *          - bearerAuth: []
+ *      parameters:
+ *        - in: path
+ *          name: reviewId
+ *          schema:
+ *              type: string
+ *          required: true
+ *      requestBody:
+ *          required: true
+ *          content:
+ *              application/json:
+ *                  schema:
+ *                      type: object
+ *                      properties:
+ *                          comment:
+ *                              type: string
+ *                          rating:
+ *                              type: integer
+ *
+ *      responses:
+ *          200:
+ *              description: OK
+ *          403:
+ *              description: Forbiden
+ *          500:
+ *              description: Internal Server Error
+ *          429:
+ *              description: Too many requests
+ */
+reviewRouter.put("/:reviewId/update", validateReview, updateReview);
+ 
+/**
+ * @swagger
+ * /user/review/{reviewId}/remove:
+ *  delete:
+ *      summary: Delete review to a product
+ *      tags: [Reviews]
+ *      security:
+ *          - bearerAuth: []
+ *      parameters:
+ *        - in: path
+ *          name: reviewId
+ *          schema:
+ *              type: string
+ *          required: true
+ *
+ *      responses:
+ *          200:
+ *              description: OK
+ *          403:
+ *              description: Forbiden
+ *          500:
+ *              description: Internal Server Error
+ *          429:
+ *              description: Too many requests
+ */
+reviewRouter.delete("/:reviewId/remove", authCheck, deleteOwnReview);
+ 
+export default reviewRouter;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/farming-product-REST-api/src/routes/user.routes.ts.html b/coverage/farming-product-REST-api/src/routes/user.routes.ts.html new file mode 100644 index 0000000..bfd14dc --- /dev/null +++ b/coverage/farming-product-REST-api/src/routes/user.routes.ts.html @@ -0,0 +1,1174 @@ + + + + + + Code coverage report for farming-product-REST-api/src/routes/user.routes.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api/src/routes user.routes.ts

+
+ +
+ 63.33% + Statements + 19/30 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 63.33% + Lines + 19/30 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +335 +336 +337 +338 +339 +340 +341 +342 +343 +344 +345 +346 +347 +348 +349 +350 +351 +352 +353 +354 +355 +356 +357 +358 +359 +360 +361 +362 +363 +3641x +1x +1x +  +  +  +  +  +  +  +  +1x +1x +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +1x +1x +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +1x + 
import express, { Router, Request, Response, NextFunction } from "express";
+import authCheck from "../middleware/auth-check";
+import {
+  getUserData,
+  updateUser,
+  deleteUser,
+  updatePassword,
+  updateShipAddress,
+  addExpoPushNotificationToken,
+  getAllUserData,
+} from "../controllers/user.controller";
+import uploadMiddleware from "../middleware/multerStorage";
+import { z } from "zod";
+ 
+const userRouter: Router = express.Router();
+ 
+/**
+ * @swagger
+ * tags:
+ *   name: User profile
+ *   description: API for managing user profile
+ */
+ 
+/**
+ * @swagger
+ * components:
+ *   schemas:
+ *     User:
+ *       type: object
+ *       required:
+ *         - firstName
+ *         - lastName
+ *         - email
+ *         - address
+ *         - shipAddress
+ *         - country
+ *         - imageUrl
+ *         - googleId
+ *         - facebookId
+ *         - phoneNum
+ *         - roleId
+ *         - vip
+ *         - verifiedUser
+ *       properties:
+ *         id:
+ *           type: string
+ *           format: uuid
+ *           description: The auto-generated id of the user
+ *           example: 550e8400-e29b-41d4-a716-446655440000
+ *         firstName:
+ *           type: string
+ *           description: User's first name
+ *           example: John
+ *         lastName:
+ *           type: string
+ *           description: User's last name
+ *           example: Doe
+ *         email:
+ *           type: string
+ *           format: email
+ *           description: User's email address
+ *           example: john.doe@example.com
+ *         address:
+ *           type: string
+ *           description: User's primary address
+ *           example: 123 Main Street, Douala
+ *         shipAddress:
+ *           type: array
+ *           description: List of user's shipping addresses
+ *           items:
+ *             type: object
+ *             properties:
+ *               address:
+ *                 type: string
+ *                 example: 456 Shipping Ave, Yaounde
+ *               isDefault:
+ *                 type: boolean
+ *                 example: true
+ *         country:
+ *           type: string
+ *           description: User's country
+ *           example: Cameroon
+ *         imageUrl:
+ *           type: string
+ *           description: URL to user's profile image
+ *           example: https://farmingproducts-storage.com/profiles/john-doe.jpg
+ *         googleId:
+ *           type: string
+ *           description: Google OAuth ID (if user signed up with Google)
+ *           example: 118234546580958341234
+ *         facebookId:
+ *           type: string
+ *           description: Facebook OAuth ID (if user signed up with Facebook)
+ *           example: 10229876543210987
+ *         phoneNum:
+ *           type: number
+ *           description: User's phone number
+ *           example: 237612345678
+ *         roleId:
+ *           type: string
+ *           format: uuid
+ *           description: ID of the user's role
+ *           example: 550e8400-e29b-41d4-a716-446655440001
+ *         password:
+ *           type: string
+ *           format: password
+ *           description: User's password (hashed)
+ *           example: null
+ *         expoPushToken:
+ *           type: string
+ *           description: Token for push notifications
+ *           example: ExponentPushToken[xxxxxxxxxxxxxxxxxxxxxx]
+ *         vip:
+ *           type: boolean
+ *           description: Indicates if the user has VIP status
+ *           example: false
+ *         verifiedUser:
+ *           type: boolean
+ *           description: Indicates if the user's identity has been verified
+ *           example: true
+ *         createdAt:
+ *           type: string
+ *           format: date-time
+ *           description: The date the user was created
+ *           example: 2023-03-15T12:00:00Z
+ *         updatedAt:
+ *           type: string
+ *           format: date-time
+ *           description: The date the user was last updated
+ *           example: 2023-03-16T14:30:00Z
+ */
+ 
+// Zod schema for updatePassword endpoint
+const updatePasswordSchema = z.object({
+  password: z.string().min(8, "Password must be at least 8 characters"),
+  userId: z.string().uuid("Invalid userId format"),
+});
+ 
+function validateUpdatePassword(
+  req: Request,
+  res: Response,
+  next: NextFunction,
+) {
+  const result = updatePasswordSchema.safeParse(req.body);
+  if (!result.success) {
+    return res.status(400).json({ errors: result.error.flatten().fieldErrors });
+  }
+  next();
+}
+ 
+// update user password
+ 
+/**
+ * @swagger
+ * /user/updatePassword/:
+ *  put:
+ *      summary: api to update user password
+ *      tags: [User profile]
+ *      requestBody:
+ *          required: true
+ *          content:
+ *              application/json:
+ *                  schema:
+ *                      type: object
+ *                      properties:
+ *                          password:
+ *                              type: string
+ *                          userId:
+ *                              type: string
+ *      responses:
+ *          200:
+ *              description: OK
+ *          500:
+ *              description: Server Error
+ *
+ */
+userRouter.put("/updatePassword/", validateUpdatePassword, updatePassword);
+ 
+// apply the auth middleware to all the routes below
+userRouter.use(authCheck);
+ 
+/**
+ * @swagger
+ * components:
+ *  securitySchemes:
+ *      bearerAuth:
+ *          type: http
+ *          scheme: bearer
+ *          bearerFormat: JWT
+ *  security:
+ *      - bearerAuth: []
+ */
+ 
+/**
+ * @swagger
+ * /user:
+ *  get:
+ *      summary: get all users
+ *      tags: [User profile]
+ *      security:
+ *          - bearerAuth: []
+ *      responses:
+ *          200:
+ *              description: OK
+ *          500:
+ *              description: server error
+ */
+ 
+userRouter.get("/", getAllUserData);
+ 
+/**
+ * @swagger
+ * /user/{userId}:
+ *  get:
+ *      summary: get logged a user data
+ *      tags: [User profile]
+ *      security:
+ *          - bearerAuth: []
+ *      responses:
+ *          200:
+ *              description: OK
+ *          500:
+ *              description: server error
+ */
+ 
+userRouter.get("/:userId", getUserData);
+ 
+/**
+ * @swagger
+ * /user/{userId}:
+ *   put:
+ *     summary: Update user profile (with image upload)
+ *     tags: [User profile]
+ *     security:
+ *       - bearerAuth: []
+ *     parameters:
+ *       - in: path
+ *         name: userId
+ *         schema:
+ *           type: string
+ *         required: true
+ *     requestBody:
+ *       required: true
+ *       content:
+ *         multipart/form-data:
+ *           schema:
+ *             type: object
+ *             properties:
+ *               firstName:
+ *                 type: string
+ *               lastName:
+ *                 type: string
+ *               address:
+ *                 type: string
+ *               profileImage:
+ *                 type: string
+ *                 format: binary
+ *     responses:
+ *       200:
+ *         description: User updated successfully
+ *       400:
+ *         description: Validation error
+ *         content:
+ *           application/json:
+ *             schema:
+ *               type: object
+ *               properties:
+ *                 errors:
+ *                   type: object
+ *                   example: { firstName: ["Required"] }
+ *       500:
+ *         description: Internal Server Error
+ */
+userRouter.put("/:userId", uploadMiddleware("profileImage"), updateUser);
+ 
+/**
+ * @swagger
+ * /user/{userId}/shipAddress:
+ *  put:
+ *      summary: api to update user shipping address
+ *      tags: [User profile]
+ *      security:
+ *          - bearerAuth: []
+ *      parameters:
+ *        - in: path
+ *          name: userId
+ *          schema:
+ *              type: string
+ *          required: true
+ *      requestBody:
+ *          required: true
+ *          content:
+ *            application/json:
+ *                  schema:
+ *                      type: object
+ *                      properties:
+ *                          shipAddress:
+ *                              type: string
+ *
+ *      responses:
+ *          200:
+ *              description: OK
+ *          500:
+ *              description: Server Error
+ *
+ */
+userRouter.put("/:userId/shipAddress", updateShipAddress);
+/**
+ * @swagger
+ * /user/{userId}/expoPushToken:
+ *  put:
+ *      summary: api to update user data via expo push token
+ *      tags: [User profile]
+ *      security:
+ *          - bearerAuth: []
+ *      parameters:
+ *        - in: path
+ *          name: userId
+ *          schema:
+ *              type: string
+ *          required: true
+ *      requestBody:
+ *          required: true
+ *          content:
+ *             application/json:
+ *                  schema:
+ *                      type: object
+ *                      properties:
+ *                          expoPushToken:
+ *                              type: string
+ *
+ *      responses:
+ *          200:
+ *              description: OK
+ *          500:
+ *              description: Server Error
+ *
+ */
+userRouter.put("/:userId/expoPushToken", addExpoPushNotificationToken);
+ 
+/**
+ * @swagger
+ * /user/{userId}:
+ *  delete:
+ *      summary: Caution!! Deleting Profile
+ *      tags: [User profile]
+ *      security:
+ *          - bearerAuth: []
+ *      parameters:
+ *        - in: path
+ *          name: userId
+ *          schema:
+ *              type: string
+ *          required: true
+ *      responses:
+ *          200:
+ *              description: OK
+ *          500:
+ *              description: Server error
+ */
+userRouter.delete("/:userId", deleteUser);
+ 
+export default userRouter;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/farming-product-REST-api/src/utils/index.html b/coverage/farming-product-REST-api/src/utils/index.html new file mode 100644 index 0000000..006bb63 --- /dev/null +++ b/coverage/farming-product-REST-api/src/utils/index.html @@ -0,0 +1,116 @@ + + + + + + Code coverage report for farming-product-REST-api/src/utils + + + + + + + + + +
+
+

All files farming-product-REST-api/src/utils

+
+ +
+ 0% + Statements + 0/19 +
+ + +
+ 100% + Branches + 1/1 +
+ + +
+ 100% + Functions + 1/1 +
+ + +
+ 0% + Lines + 0/19 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
runMigrations.ts +
+
0%0/19100%1/1100%1/10%0/19
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/farming-product-REST-api/src/utils/runMigrations.ts.html b/coverage/farming-product-REST-api/src/utils/runMigrations.ts.html new file mode 100644 index 0000000..5402e9e --- /dev/null +++ b/coverage/farming-product-REST-api/src/utils/runMigrations.ts.html @@ -0,0 +1,169 @@ + + + + + + Code coverage report for farming-product-REST-api/src/utils/runMigrations.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api/src/utils runMigrations.ts

+
+ +
+ 0% + Statements + 0/19 +
+ + +
+ 100% + Branches + 1/1 +
+ + +
+ 100% + Functions + 1/1 +
+ + +
+ 0% + Lines + 0/19 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import { Sequelize } from "sequelize-typescript";
+import { Umzug, SequelizeStorage } from "umzug";
+ 
+/**
+ * Run database migrations
+ * @param sequelize - Sequelize instance
+ * @returns Promise<void>
+ */
+const runMigrations = async (sequelize: Sequelize): Promise<void> => {
+  const umzug = new Umzug({
+    migrations: {
+      glob: ["migrations/*.js", { cwd: process.cwd() }],
+    },
+    context: sequelize.getQueryInterface(),
+    storage: new SequelizeStorage({ sequelize }),
+    logger: console,
+  });
+ 
+  try {
+    await umzug.up();
+    console.log("✅ All migrations have been executed successfully.");
+  } catch (error) {
+    console.error("❌ Error running migrations:", error);
+    throw error;
+  }
+};
+ 
+export default runMigrations;
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/favicon.png b/coverage/favicon.png new file mode 100644 index 0000000..c1525b8 Binary files /dev/null and b/coverage/favicon.png differ diff --git a/coverage/index.html b/coverage/index.html new file mode 100644 index 0000000..1d99e42 --- /dev/null +++ b/coverage/index.html @@ -0,0 +1,221 @@ + + + + + + Code coverage report for All files + + + + + + + + + +
+
+

All files

+
+ +
+ 27.05% + Statements + 950/3511 +
+ + +
+ 53.65% + Branches + 22/41 +
+ + +
+ 11.47% + Functions + 7/61 +
+ + +
+ 27.05% + Lines + 950/3511 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
farming-product-REST-api +
+
88.07%96/10925%1/4100%0/088.07%96/109
farming-product-REST-api/src/config +
+
0%0/250%0/10%0/10%0/25
farming-product-REST-api/src/controllers +
+
7.8%177/226945%9/204.87%2/417.8%177/2269
farming-product-REST-api/src/errors +
+
100%10/10100%3/3100%1/1100%10/10
farming-product-REST-api/src/middleware +
+
26.07%67/25750%3/630%3/1026.07%67/257
farming-product-REST-api/src/models +
+
73.21%440/60183.33%5/60%0/173.21%440/601
farming-product-REST-api/src/routes +
+
72.39%160/221100%0/00%0/672.39%160/221
farming-product-REST-api/src/utils +
+
0%0/19100%1/1100%1/10%0/19
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/base.css b/coverage/lcov-report/base.css new file mode 100644 index 0000000..f418035 --- /dev/null +++ b/coverage/lcov-report/base.css @@ -0,0 +1,224 @@ +body, html { + margin:0; padding: 0; + height: 100%; +} +body { + font-family: Helvetica Neue, Helvetica, Arial; + font-size: 14px; + color:#333; +} +.small { font-size: 12px; } +*, *:after, *:before { + -webkit-box-sizing:border-box; + -moz-box-sizing:border-box; + box-sizing:border-box; + } +h1 { font-size: 20px; margin: 0;} +h2 { font-size: 14px; } +pre { + font: 12px/1.4 Consolas, "Liberation Mono", Menlo, Courier, monospace; + margin: 0; + padding: 0; + -moz-tab-size: 2; + -o-tab-size: 2; + tab-size: 2; +} +a { color:#0074D9; text-decoration:none; } +a:hover { text-decoration:underline; } +.strong { font-weight: bold; } +.space-top1 { padding: 10px 0 0 0; } +.pad2y { padding: 20px 0; } +.pad1y { padding: 10px 0; } +.pad2x { padding: 0 20px; } +.pad2 { padding: 20px; } +.pad1 { padding: 10px; } +.space-left2 { padding-left:55px; } +.space-right2 { padding-right:20px; } +.center { text-align:center; } +.clearfix { display:block; } +.clearfix:after { + content:''; + display:block; + height:0; + clear:both; + visibility:hidden; + } +.fl { float: left; } +@media only screen and (max-width:640px) { + .col3 { width:100%; max-width:100%; } + .hide-mobile { display:none!important; } +} + +.quiet { + color: #7f7f7f; + color: rgba(0,0,0,0.5); +} +.quiet a { opacity: 0.7; } + +.fraction { + font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; + font-size: 10px; + color: #555; + background: #E8E8E8; + padding: 4px 5px; + border-radius: 3px; + vertical-align: middle; +} + +div.path a:link, div.path a:visited { color: #333; } +table.coverage { + border-collapse: collapse; + margin: 10px 0 0 0; + padding: 0; +} + +table.coverage td { + margin: 0; + padding: 0; + vertical-align: top; +} +table.coverage td.line-count { + text-align: right; + padding: 0 5px 0 20px; +} +table.coverage td.line-coverage { + text-align: right; + padding-right: 10px; + min-width:20px; +} + +table.coverage td span.cline-any { + display: inline-block; + padding: 0 5px; + width: 100%; +} +.missing-if-branch { + display: inline-block; + margin-right: 5px; + border-radius: 3px; + position: relative; + padding: 0 4px; + background: #333; + color: yellow; +} + +.skip-if-branch { + display: none; + margin-right: 10px; + position: relative; + padding: 0 4px; + background: #ccc; + color: white; +} +.missing-if-branch .typ, .skip-if-branch .typ { + color: inherit !important; +} +.coverage-summary { + border-collapse: collapse; + width: 100%; +} +.coverage-summary tr { border-bottom: 1px solid #bbb; } +.keyline-all { border: 1px solid #ddd; } +.coverage-summary td, .coverage-summary th { padding: 10px; } +.coverage-summary tbody { border: 1px solid #bbb; } +.coverage-summary td { border-right: 1px solid #bbb; } +.coverage-summary td:last-child { border-right: none; } +.coverage-summary th { + text-align: left; + font-weight: normal; + white-space: nowrap; +} +.coverage-summary th.file { border-right: none !important; } +.coverage-summary th.pct { } +.coverage-summary th.pic, +.coverage-summary th.abs, +.coverage-summary td.pct, +.coverage-summary td.abs { text-align: right; } +.coverage-summary td.file { white-space: nowrap; } +.coverage-summary td.pic { min-width: 120px !important; } +.coverage-summary tfoot td { } + +.coverage-summary .sorter { + height: 10px; + width: 7px; + display: inline-block; + margin-left: 0.5em; + background: url(sort-arrow-sprite.png) no-repeat scroll 0 0 transparent; +} +.coverage-summary .sorted .sorter { + background-position: 0 -20px; +} +.coverage-summary .sorted-desc .sorter { + background-position: 0 -10px; +} +.status-line { height: 10px; } +/* yellow */ +.cbranch-no { background: yellow !important; color: #111; } +/* dark red */ +.red.solid, .status-line.low, .low .cover-fill { background:#C21F39 } +.low .chart { border:1px solid #C21F39 } +.highlighted, +.highlighted .cstat-no, .highlighted .fstat-no, .highlighted .cbranch-no{ + background: #C21F39 !important; +} +/* medium red */ +.cstat-no, .fstat-no, .cbranch-no, .cbranch-no { background:#F6C6CE } +/* light red */ +.low, .cline-no { background:#FCE1E5 } +/* light green */ +.high, .cline-yes { background:rgb(230,245,208) } +/* medium green */ +.cstat-yes { background:rgb(161,215,106) } +/* dark green */ +.status-line.high, .high .cover-fill { background:rgb(77,146,33) } +.high .chart { border:1px solid rgb(77,146,33) } +/* dark yellow (gold) */ +.status-line.medium, .medium .cover-fill { background: #f9cd0b; } +.medium .chart { border:1px solid #f9cd0b; } +/* light yellow */ +.medium { background: #fff4c2; } + +.cstat-skip { background: #ddd; color: #111; } +.fstat-skip { background: #ddd; color: #111 !important; } +.cbranch-skip { background: #ddd !important; color: #111; } + +span.cline-neutral { background: #eaeaea; } + +.coverage-summary td.empty { + opacity: .5; + padding-top: 4px; + padding-bottom: 4px; + line-height: 1; + color: #888; +} + +.cover-fill, .cover-empty { + display:inline-block; + height: 12px; +} +.chart { + line-height: 0; +} +.cover-empty { + background: white; +} +.cover-full { + border-right: none !important; +} +pre.prettyprint { + border: none !important; + padding: 0 !important; + margin: 0 !important; +} +.com { color: #999 !important; } +.ignore-none { color: #999; font-weight: normal; } + +.wrapper { + min-height: 100%; + height: auto !important; + height: 100%; + margin: 0 auto -48px; +} +.footer, .push { + height: 48px; +} diff --git a/coverage/lcov-report/block-navigation.js b/coverage/lcov-report/block-navigation.js new file mode 100644 index 0000000..cc12130 --- /dev/null +++ b/coverage/lcov-report/block-navigation.js @@ -0,0 +1,87 @@ +/* eslint-disable */ +var jumpToCode = (function init() { + // Classes of code we would like to highlight in the file view + var missingCoverageClasses = ['.cbranch-no', '.cstat-no', '.fstat-no']; + + // Elements to highlight in the file listing view + var fileListingElements = ['td.pct.low']; + + // We don't want to select elements that are direct descendants of another match + var notSelector = ':not(' + missingCoverageClasses.join('):not(') + ') > '; // becomes `:not(a):not(b) > ` + + // Selecter that finds elements on the page to which we can jump + var selector = + fileListingElements.join(', ') + + ', ' + + notSelector + + missingCoverageClasses.join(', ' + notSelector); // becomes `:not(a):not(b) > a, :not(a):not(b) > b` + + // The NodeList of matching elements + var missingCoverageElements = document.querySelectorAll(selector); + + var currentIndex; + + function toggleClass(index) { + missingCoverageElements + .item(currentIndex) + .classList.remove('highlighted'); + missingCoverageElements.item(index).classList.add('highlighted'); + } + + function makeCurrent(index) { + toggleClass(index); + currentIndex = index; + missingCoverageElements.item(index).scrollIntoView({ + behavior: 'smooth', + block: 'center', + inline: 'center' + }); + } + + function goToPrevious() { + var nextIndex = 0; + if (typeof currentIndex !== 'number' || currentIndex === 0) { + nextIndex = missingCoverageElements.length - 1; + } else if (missingCoverageElements.length > 1) { + nextIndex = currentIndex - 1; + } + + makeCurrent(nextIndex); + } + + function goToNext() { + var nextIndex = 0; + + if ( + typeof currentIndex === 'number' && + currentIndex < missingCoverageElements.length - 1 + ) { + nextIndex = currentIndex + 1; + } + + makeCurrent(nextIndex); + } + + return function jump(event) { + if ( + document.getElementById('fileSearch') === document.activeElement && + document.activeElement != null + ) { + // if we're currently focused on the search input, we don't want to navigate + return; + } + + switch (event.which) { + case 78: // n + case 74: // j + goToNext(); + break; + case 66: // b + case 75: // k + case 80: // p + goToPrevious(); + break; + } + }; +})(); +window.addEventListener('keydown', jumpToCode); diff --git a/coverage/lcov-report/farming-product-REST-api/app.ts.html b/coverage/lcov-report/farming-product-REST-api/app.ts.html new file mode 100644 index 0000000..cc4aa80 --- /dev/null +++ b/coverage/lcov-report/farming-product-REST-api/app.ts.html @@ -0,0 +1,562 @@ + + + + + + Code coverage report for farming-product-REST-api/app.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api app.ts

+
+ +
+ 88.07% + Statements + 96/109 +
+ + +
+ 25% + Branches + 1/4 +
+ + +
+ 100% + Functions + 0/0 +
+ + +
+ 88.07% + Lines + 96/109 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160  +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +  +  +1x +1x +  +1x +  +1x +  +1x +  +  +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +  +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +  +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +  +  +1x +  +  +1x +  +  +1x +  +  +  +  +1x +1x +1x +  +1x +1x +1x +  +  +1x +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +1x +1x +1x +  +  +1x +  +  +1x +  +  +  +  +  +  +  +  +  +  +1x + 
// app.ts
+import dotenv from "dotenv";
+import express, { Application, Request, Response, NextFunction } from "express";
+import bodyParser from "body-parser";
+import limiter from "./src/middleware/rateLimiter";
+import appRouter from "./src/routes";
+import cors from "cors";
+import path from "path";
+import fs from "fs";
+import errorHandler from "./src/middleware/errorHandler";
+import swaggerUi from "swagger-ui-express";
+import swaggerJsDoc from "swagger-jsdoc";
+import helmet from "helmet";
+import AppError from "./src/errors/customErrors";
+ 
+// Define the server configuration
+const port: number = parseInt(process.env.PORT || "3000", 10);
+const hostname: string = process.env.DB_HOST || "localhost";
+ 
+const isDev = process.env.NODE_ENV !== "production";
+ 
+dotenv.config();
+ 
+const app: Application = express();
+ 
+// Swagger options
+const doc: any = {
+  definition: {
+    openapi: "3.0.0",
+    info: {
+      title: "Farming products API",
+      version: "1.0.0",
+      description: "API Documentation for Farming products",
+      contact: {
+        name: "Farming products",
+      }
+    },
+    servers: [
+      {
+        url: "http://localhost:3000/api/v1",
+        description: "Development on Local server",
+      }
+    ],
+    components: {
+      securitySchemes: {
+        bearerAuth: {
+          type: "http",
+          scheme: "bearer",
+          bearerFormat: "JWT",
+        },
+      },
+      schemas: {
+        // Reference your main models here for Swagger UI
+        Product: {
+          type: "object",
+          required: ["productName", "productCat", "priceType", "price"],
+          properties: {
+            productName: { type: "string" },
+            productCat: { type: "string" },
+            priceType: { type: "string" },
+            price: { type: "number" },
+            description: { type: "string" },
+            wholeSale: { type: "boolean" },
+            imageUrl: { type: "string" },
+            userId: { type: "string" },
+          },
+          example: {
+            productName: "Tomato",
+            productCat: "Vegetable",
+            priceType: "per kg",
+            price: 1000,
+            description: "Fresh tomatoes",
+            wholeSale: false,
+            imageUrl: "https://cloudinary.com/image.jpg",
+            userId: "uuid-1234"
+          }
+        },
+        // Add more models as needed
+      },
+    },
+    security: [
+      {
+        bearerAuth: [],
+      },
+    ],
+  },
+  apis: isDev ? ["./src/routes/*.ts"] : ["./dist/routes/*.js"]
+};
+ 
+// Update this to '.ts' as files are converted to TypeScript
+const swaggerSpec = swaggerJsDoc(doc);
+// app.use("/api-docs", swaggerUi.serve, swaggerUi.setup(swaggerJsDoc(doc)));
+ 
+const imagesDir = path.join(__dirname, "/src/public/images");
+ 
+// Ensure the directory exists at app start
+if (!fs.existsSync(imagesDir)) {
+  fs.mkdirSync(imagesDir, { recursive: true });
+}
+ 
+// Middleware setup
+app.use(limiter);
+app.set("trust proxy", 1);
+app.use(helmet());
+ 
+app.use(bodyParser.json());
+app.use(cors());
+app.use("/public/images", express.static("public/images"));
+ 
+// Routes setup
+app.use("/api/v1", appRouter);
+ 
+// @ts-ignore-next-line
+app.use("/api-docs", swaggerUi.serve, swaggerUi.setup(swaggerSpec));
+ 
+// Swagger default route definition
+/**
+ * @swagger
+ * /:
+ *   get:
+ *     summary: Welcome message
+ *     description: Returns a greeting message
+ *     responses:
+ *       200:
+ *         description: A simple greeting message.
+ *         content:
+ *           application/json:
+ *             schema:
+ *               type: object
+ *               properties:
+ *                 message:
+ *                   type: string
+ *                   example: "Hello World!! Farming products_2"
+ */
+app.get("/", (req: Request, res: Response) => {
+  res.send("Hello World!! Farming products_2");
+});
+ 
+// 404 handler
+app.use((req: Request, res: Response, next: NextFunction) => {
+  next(new AppError(`Not Found - ${req.originalUrl}`, 404));
+});
+ 
+// Error handling middleware - must be last
+app.use(errorHandler);
+ 
+// Only start the server if we're not in a test environment
+if (process.env.NODE_ENV !== 'test') {
+  app.listen(port, async () => {
+    try {
+      console.log("✅ Database is up to date.");
+    } catch (error) {
+      console.error("❌ Unable to connect to the database:", error);
+    }
+    console.log(`Server running on http://localhost:${port}`);
+  });
+}
+ 
+export default app;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/farming-product-REST-api/index.html b/coverage/lcov-report/farming-product-REST-api/index.html new file mode 100644 index 0000000..4e86aba --- /dev/null +++ b/coverage/lcov-report/farming-product-REST-api/index.html @@ -0,0 +1,116 @@ + + + + + + Code coverage report for farming-product-REST-api + + + + + + + + + +
+
+

All files farming-product-REST-api

+
+ +
+ 88.07% + Statements + 96/109 +
+ + +
+ 25% + Branches + 1/4 +
+ + +
+ 100% + Functions + 0/0 +
+ + +
+ 88.07% + Lines + 96/109 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
app.ts +
+
88.07%96/10925%1/4100%0/088.07%96/109
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/farming-product-REST-api/src/config/config.ts.html b/coverage/lcov-report/farming-product-REST-api/src/config/config.ts.html new file mode 100644 index 0000000..527022b --- /dev/null +++ b/coverage/lcov-report/farming-product-REST-api/src/config/config.ts.html @@ -0,0 +1,166 @@ + + + + + + Code coverage report for farming-product-REST-api/src/config/config.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api/src/config config.ts

+
+ +
+ 0% + Statements + 0/25 +
+ + +
+ 0% + Branches + 0/1 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 0% + Lines + 0/25 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import dotenv from "dotenv";
+ 
+dotenv.config();
+ 
+export default {
+  development: {
+    username: process.env.DB_USERNAME,
+    password: process.env.DB_PASSWORD,
+    database: process.env.DB_NAME,
+    host: process.env.DB_HOST,
+    dialect: "postgres",
+  },
+  test: {
+    username: process.env.DB_USERNAME,
+    password: process.env.DB_PASSWORD,
+    database: process.env.DB_NAME,
+    host: process.env.DB_HOST,
+    dialect: "postgres",
+  },
+  production: {
+    username: process.env.DB_USERNAME,
+    password: process.env.DB_PASSWORD,
+    database: process.env.DB_NAME,
+    host: process.env.DB_HOST,
+    dialect: "postgres",
+  },
+};
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/farming-product-REST-api/src/config/index.html b/coverage/lcov-report/farming-product-REST-api/src/config/index.html new file mode 100644 index 0000000..8ef624d --- /dev/null +++ b/coverage/lcov-report/farming-product-REST-api/src/config/index.html @@ -0,0 +1,116 @@ + + + + + + Code coverage report for farming-product-REST-api/src/config + + + + + + + + + +
+
+

All files farming-product-REST-api/src/config

+
+ +
+ 0% + Statements + 0/25 +
+ + +
+ 0% + Branches + 0/1 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 0% + Lines + 0/25 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
config.ts +
+
0%0/250%0/10%0/10%0/25
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/farming-product-REST-api/src/controllers/auth.controller.ts.html b/coverage/lcov-report/farming-product-REST-api/src/controllers/auth.controller.ts.html new file mode 100644 index 0000000..a053cbd --- /dev/null +++ b/coverage/lcov-report/farming-product-REST-api/src/controllers/auth.controller.ts.html @@ -0,0 +1,2131 @@ + + + + + + Code coverage report for farming-product-REST-api/src/controllers/auth.controller.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api/src/controllers auth.controller.ts

+
+ +
+ 18.21% + Statements + 94/516 +
+ + +
+ 50% + Branches + 9/18 +
+ + +
+ 22.22% + Functions + 2/9 +
+ + +
+ 18.21% + Lines + 94/516 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +335 +336 +337 +338 +339 +340 +341 +342 +343 +344 +345 +346 +347 +348 +349 +350 +351 +352 +353 +354 +355 +356 +357 +358 +359 +360 +361 +362 +363 +364 +365 +366 +367 +368 +369 +370 +371 +372 +373 +374 +375 +376 +377 +378 +379 +380 +381 +382 +383 +384 +385 +386 +387 +388 +389 +390 +391 +392 +393 +394 +395 +396 +397 +398 +399 +400 +401 +402 +403 +404 +405 +406 +407 +408 +409 +410 +411 +412 +413 +414 +415 +416 +417 +418 +419 +420 +421 +422 +423 +424 +425 +426 +427 +428 +429 +430 +431 +432 +433 +434 +435 +436 +437 +438 +439 +440 +441 +442 +443 +444 +445 +446 +447 +448 +449 +450 +451 +452 +453 +454 +455 +456 +457 +458 +459 +460 +461 +462 +463 +464 +465 +466 +467 +468 +469 +470 +471 +472 +473 +474 +475 +476 +477 +478 +479 +480 +481 +482 +483 +484 +485 +486 +487 +488 +489 +490 +491 +492 +493 +494 +495 +496 +497 +498 +499 +500 +501 +502 +503 +504 +505 +506 +507 +508 +509 +510 +511 +512 +513 +514 +515 +516 +517 +518 +519 +520 +521 +522 +523 +524 +525 +526 +527 +528 +529 +530 +531 +532 +533 +534 +535 +536 +537 +538 +539 +540 +541 +542 +543 +544 +545 +546 +547 +548 +549 +550 +551 +552 +553 +554 +555 +556 +557 +558 +559 +560 +561 +562 +563 +564 +565 +566 +567 +568 +569 +570 +571 +572 +573 +574 +575 +576 +577 +578 +579 +580 +581 +582 +583 +584 +585 +586 +587 +588 +589 +590 +591 +592 +593 +594 +595 +596 +597 +598 +599 +600 +601 +602 +603 +604 +605 +606 +607 +608 +609 +610 +611 +612 +613 +614 +615 +616 +617 +618 +619 +620 +621 +622 +623 +624 +625 +626 +627 +628 +629 +630 +631 +632 +633 +634 +635 +636 +637 +638 +639 +640 +641 +642 +643 +644 +645 +646 +647 +648 +649 +650 +651 +652 +653 +654 +655 +656 +657 +658 +659 +660 +661 +662 +663 +664 +665 +666 +667 +668 +669 +670 +671 +672 +673 +674 +675 +676 +677 +678 +679 +680 +681 +682 +6831x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +  +1x +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +1x +1x +1x +1x +  +1x +  +  +1x +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +2x +2x +2x +2x +2x +2x +  +  +2x +1x +1x +  +2x +  +  +  +2x +  +  +  +1x +1x +1x +1x +2x +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +2x +2x +2x +2x +2x +2x +2x +2x +2x +2x +2x +2x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +2x +2x +2x +2x +2x +2x +2x +2x +2x +  +  +2x +2x +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +1x +1x +1x +1x +1x +1x +  +1x +1x +1x +1x +1x +1x +1x +1x +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +1x +1x +1x +1x +1x +1x +1x +1x +  +  +1x +1x +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import User from "../models/user";
+import Role from "../models/role";
+import UserOTPCode from "../models/userotpcode";
+import { hashSync, compare } from "bcryptjs";
+import { randomInt } from "crypto";
+import jwt from "jsonwebtoken";
+import nodemailer from "nodemailer";
+import { v2 as cloudinary } from "cloudinary";
+import { v4 as uuidv4 } from "uuid";
+import { OAuth2Client } from "google-auth-library";
+import axios from "axios";
+import { NextFunction, Request, Response } from "express";
+import { Readable } from "stream";
+import AppError from "../errors/customErrors";
+ 
+// Type definitions
+interface LoginRequest {
+  email: string;
+  password: string;
+}
+ 
+interface VerifyPhoneRequest {
+  phoneNum: string;
+  password: string;
+  country: string;
+  email: string;
+  userRole: string;
+}
+ 
+interface RegisterUserRequest {
+  firstName: string;
+  lastName: string;
+  address: string;
+  expoPushToken?: string;
+}
+ 
+interface ShippingAddress {
+  id: string;
+  title: string;
+  address: string;
+  default: boolean;
+}
+ 
+interface VerifyOtpRequest {
+  email: string;
+  otp: string;
+}
+ 
+interface AuthHandlerRequest {
+  googleToken?: string;
+  facebookToken?: string;
+}
+ 
+interface SendOtpRequest {
+  email: string;
+}
+ 
+interface GooglePayload {
+  sub: string;
+  email?: string;
+  given_name?: string;
+  family_name?: string;
+  picture?: string;
+}
+ 
+// Configure cloudinary
+cloudinary.config({
+  cloud_name: process.env.CLOUDINARY_CLOUD_NAME,
+  api_key: process.env.CLOUDINARY_API_KEY,
+  api_secret: process.env.CLOUDINARY_API_SECRET,
+});
+ 
+const googleClient = new OAuth2Client(process.env.GMAIL_AUTH_CLIENTID);
+ 
+// Utility function for generating OTP
+const generateOTP = () => randomInt(1000, 9999).toString();
+ 
+// Utility function for sending OTP
+const sendOTP = async (email: string, otp: string, phone: string) => {
+  if (email) {
+    const transporter = nodemailer.createTransport({
+      service: process.env.FARMING_PRODUCTS_PROVIDER,
+      host: process.env.FARMING_PRODUCTS_HOST,
+      port: 587,
+      secure: false,
+      auth: {
+        user: process.env.GMAIL_USER,
+        pass: process.env.GMAIL_APP_PASSWORD,
+      },
+    });
+ 
+    const mailOptions = {
+      from: `Farming products <farmingproductscmr@gmail.com>`,
+      to: email,
+      subject: "Verify Your Email",
+      html: `
+        <h1>Farming products</h1>
+        <p>Hello ${email},</p>
+        <p>Enter <b>${otp}</b> in the app to verify your email address and complete verification:</p>
+        <p>This code <b>expires in 10 minutes</b>.</p>
+        <p>If you did not request this, please ignore this email.</p>
+        <p>Thanks,<br>Farming products Team</p>
+      `,
+    };
+ 
+    await transporter.sendMail(mailOptions);
+  }
+ 
+  if (phone) {
+    const data = {
+      user: process.env.FARMING_PRODUCTS_SMS_USER,
+      password: process.env.FARMING_PRODUCTS_SMS_PASSWORD,
+      senderid: "Farming products",
+      sms: `Farming products. Your OTP is ${otp}, valid for 10 minutes only.`,
+      mobiles: phone,
+    };
+ 
+    const config = {
+      method: "post",
+      url: "https://smsvas.com/bulk/public/index.php/api/v1/sendsms",
+      headers: {
+        "Content-Type": "application/json",
+      },
+      data: JSON.stringify(data),
+    };
+ 
+    await axios.request(config);
+  }
+};
+ 
+// Verify Phone and Handle Registration
+export const verifyPhone = async (
+  req: Request<unknown, unknown, VerifyPhoneRequest>,
+  res: Response,
+  next: NextFunction,
+) => {
+  try {
+    const { phoneNum, password, country, email, userRole } = req.body;
+ 
+    // Input validation
+    if (!email || !password) {
+      throw new AppError("Empty input fields", 400);
+    }
+ 
+    if (password.length < 8) {
+      throw new AppError("Password must be at least 8 characters", 400);
+    }
+ 
+    if (!/^[\w-]+@([\w-]+\.)+[\w-]{2,4}$/.test(email)) {
+      throw new AppError("Invalid email entered", 400);
+    }
+ 
+    const validRoles = ["buyer", "farmer"] as const;
+    if (
+      !userRole ||
+      !validRoles.includes(userRole as (typeof validRoles)[number])
+    ) {
+      throw new AppError(
+        `Invalid role: ${userRole}. Valid roles are: ${validRoles.join(", ")}`,
+        400,
+      );
+    }
+ 
+    // Check if user already exists
+    const userExists = await User.findOne({ where: { email } });
+    if (userExists) {
+      throw new AppError("This email is already registered.", 400);
+    }
+ 
+    // Find or create role
+    let role = await Role.findOne({ where: { roleName: userRole } });
+    if (!role) {
+      role = await Role.create({ roleName: userRole });
+    }
+ 
+    // Hash password
+    const hashedPassword = hashSync(password.trim(), 10);
+ 
+    // Create user
+    const user = await User.create({
+      roleId: role.id,
+      email,
+      password: hashedPassword,
+      country,
+      phoneNum: parseInt(phoneNum, 10) || 0,
+      firstName: "",
+      lastName: "",
+      imageUrl: "",
+      address: "",
+      googleId: "",
+      verifiedUser: false,
+      vip: false,
+      facebookId: "",
+      shipAddress: [],
+      createdAt: new Date(),
+      updatedAt: new Date(),
+    });
+ 
+    if (!user) {
+      throw new AppError("Failed to create user during registration", 500);
+    }
+ 
+    // Generate and store OTP
+    const otp = generateOTP();
+    const hashedOtp = hashSync(otp, 10);
+ 
+    // Create OTP object
+    const otpObj = await UserOTPCode.create({
+      userId: user.id,
+      otp: hashedOtp,
+      expiredAt: new Date(Date.now() + 10 * 60 * 1000), // 10 minutes expiry
+      createdAt: new Date(),
+      updatedAt: new Date(),
+    });
+ 
+    if (!otpObj) {
+      throw new AppError("Failed to create OTP in the database", 500);
+    }
+ 
+    // Send OTP via email and/or SMS
+    await sendOTP(email, otp, phoneNum);
+ 
+    // Return success response
+    return res.status(200).json({
+      message: "OTP sent successfully, please verify the OTP",
+      email: user.email,
+      phoneNum: user.phoneNum,
+      userID: user.id,
+    });
+  } catch (error) {
+    if (error instanceof Error) {
+      next(
+        new AppError(
+          error.message,
+          error instanceof AppError ? error.statusCode : 500,
+        ),
+      );
+    } else {
+      next(new AppError("An unexpected error occurred", 500));
+    }
+  }
+};
+ 
+// Register User
+export const register_user = async (
+  req: Request<{ userId: string }, unknown, RegisterUserRequest>,
+  res: Response,
+  next: NextFunction,
+) => {
+  try {
+    req.file = {
+      path: "https://randomuser.me/api/portraits/men/1.jpg",
+      originalname: "profile.jpg",
+      mimetype: "image/jpeg",
+      size: 12345,
+      fieldname: "image",
+      encoding: "7bit",
+      stream: new Readable(),
+      destination: "",
+      buffer: Buffer.from([]),
+      filename: "profile.jpg",
+    };
+ 
+    const { userId } = req.params;
+    const { address, expoPushToken } = req.body;
+    const firstName = req.body.firstName.trim();
+    const lastName = req.body.lastName.trim();
+ 
+    const shippAddress: ShippingAddress[] = [
+      {
+        id: uuidv4(),
+        title: "Home",
+        address,
+        default: true,
+      },
+    ];
+ 
+    let imageUrl = "";
+    if (req.file) {
+      const cloudinaryImageUpload = await cloudinary.uploader.upload(
+        req.file.path,
+      );
+      imageUrl = cloudinaryImageUpload.secure_url;
+    }
+ 
+    await User.update(
+      {
+        firstName,
+        lastName,
+        address,
+        imageUrl,
+        shipAddress: shippAddress,
+        expoPushToken,
+        updatedAt: new Date(),
+      },
+      { where: { id: userId } },
+    );
+ 
+    return res.status(200).json({ message: "User successfully registered" });
+  } catch (error) {
+    next(error);
+  }
+};
+ 
+// Verify OTP
+export const verifyOtp = async (
+  req: Request<unknown, unknown, VerifyOtpRequest>,
+  res: Response,
+) => {
+  const { email, otp } = req.body;
+ 
+  try {
+    const currentUser = await User.findOne({ where: { email } });
+    if (!currentUser) {
+      return res
+        .status(401)
+        .json({ message: "The User does not exist. Please sign up!" });
+    }
+ 
+    const getOtp = await UserOTPCode.findOne({
+      where: { userId: currentUser.id },
+    });
+    if (!getOtp) {
+      return res.status(404).json({
+        message: "Server Error! Please Try Again Later by signing up.",
+      });
+    }
+ 
+    const { expiredAt } = getOtp;
+    if (expiredAt < Date.now()) {
+      await UserOTPCode.destroy({ where: { userId: currentUser.id } });
+      return res
+        .status(404)
+        .json({ message: "Code has expired. Please request again" });
+    }
+ 
+    const validOtp = await compare(otp, getOtp.otp);
+    if (validOtp) {
+      await UserOTPCode.destroy({ where: { userId: currentUser.id } });
+      await User.update({ verifiedUser: true }, { where: { email } });
+ 
+      currentUser.password = null;
+      return res
+        .status(200)
+        .json({ message: "Successfully verified!", userId: currentUser.id });
+    } else {
+      return res.status(403).json({ message: "Wrong OTP" });
+    }
+  } catch (error) {
+    if (error instanceof Error) {
+      return res.status(500).json({
+        message: "Internal Server Error!",
+        error: error.message,
+      });
+    }
+    return res.status(500).json({
+      message: "Internal Server Error!",
+    });
+  }
+};
+ 
+// Login
+export const logIn = async (
+  req: Request<unknown, unknown, LoginRequest>,
+  res: Response,
+  next: NextFunction,
+) => {
+  try {
+    const { email, password } = req.body;
+ 
+    const user = await User.findOne({
+      where: { email },
+      include: [
+        {
+          model: Role,
+          attributes: ["roleName"],
+        },
+      ],
+    });
+ 
+    if (!user) {
+      throw new AppError("No user exists for this email address", 403);
+    }
+ 
+    const verifyPassword = await compare(password, user.password as string);
+    if (!verifyPassword) {
+      throw new AppError("Incorrect Password", 403);
+    }
+ 
+    const token = jwt.sign(
+      {
+        UserId: user.id,
+        email: user.email,
+      },
+      process.env.JWT_SECRET as string,
+      { expiresIn: "1h" },
+    );
+ 
+    const refreshToken = jwt.sign(
+      {
+        UserId: user.id,
+        email: user.email,
+      },
+      process.env.JWT_SECRET_REFRESH as string,
+      { expiresIn: "7d" },
+    );
+ 
+    user.password = null;
+ 
+    res.status(200).json({
+      message: "Authentication Successful",
+      token,
+      refreshToken,
+      userData: user,
+    });
+  } catch (error) {
+    if (error instanceof Error) {
+      next(
+        new AppError(
+          error.message,
+          error instanceof AppError ? error.statusCode : 500,
+        ),
+      );
+    } else {
+      next(new AppError("An unexpected error occurred", 500));
+    }
+  }
+};
+ 
+// Refresh Token
+export const refreshToken = async (
+  req: Request,
+  res: Response,
+  next: NextFunction,
+) => {
+  try {
+    const authHeader = req.headers.authorization;
+ 
+    if (!authHeader || !authHeader.startsWith("Bearer ")) {
+      return res.status(401).json({
+        message: "You are either not logged in or your session has expired",
+      });
+    }
+ 
+    // Extract the token
+    const token = authHeader.split(" ")[1];
+ 
+    // Verify and decode the token
+    const decodedToken = jwt.verify(
+      token,
+      process.env.JWT_SECRET_REFRESH as string,
+    ) as jwt.JwtPayload;
+ 
+    if (!decodedToken.exp) {
+      return res.status(403).json({
+        auth: false,
+        message: "Token does not have an expiration time",
+      });
+    }
+ 
+    // Generate a new access token
+    const accessToken = jwt.sign(
+      { UserId: decodedToken.UserId, email: decodedToken.email },
+      process.env.JWT_SECRET as string,
+      { expiresIn: "1h" },
+    );
+ 
+    return res
+      .status(200)
+      .json({ message: "Token refreshed successfully", token: accessToken });
+  } catch (error) {
+    if (error instanceof jwt.JsonWebTokenError) {
+      return res
+        .status(401)
+        .json({ message: "Invalid token, please login again" });
+    }
+    next(error);
+  }
+};
+ 
+// Auth Handler (Google and Facebook Auth)
+export const authHandler = async (
+  req: Request<unknown, unknown, AuthHandlerRequest>,
+  res: Response,
+  next: NextFunction,
+) => {
+  const { googleToken, facebookToken } = req.body;
+ 
+  try {
+    if (!googleToken && !facebookToken) {
+      return res.status(400).json({ message: "Missing authentication token" });
+    }
+ 
+    // Ensure the 'buyer' role exists
+    let role = await Role.findOne({ where: { roleName: "buyer" } });
+    if (!role) {
+      role = await Role.create({ roleName: "buyer" });
+    }
+ 
+    let user;
+ 
+    // Google Authentication
+    if (googleToken) {
+      const ticket = await googleClient.verifyIdToken({
+        idToken: googleToken,
+        audience: process.env.GMAIL_AUTH_CLIENTID,
+      });
+ 
+      const payload = ticket.getPayload() as GooglePayload | undefined;
+      if (!payload) {
+        return res.status(401).json({ message: "Invalid Google token" });
+      }
+ 
+      const {
+        sub: googleId,
+        email = "",
+        given_name: firstName = "",
+        family_name: lastName = "",
+        picture: imageUrl = "",
+      } = payload;
+ 
+      user = await User.findOne({ where: { email } });
+ 
+      if (user) {
+        if (!user.googleId) {
+          user.googleId = googleId;
+          await user.save();
+        } else if (user.googleId !== googleId) {
+          return res
+            .status(400)
+            .json({ message: "Please use your exact email ID to sign in" });
+        }
+      } else {
+        user = await User.create({
+          googleId,
+          email,
+          firstName,
+          lastName,
+          imageUrl,
+          roleId: role.id,
+          shipAddress: [],
+          address: "",
+          phoneNum: 0,
+          country: "",
+          vip: false,
+          verifiedUser: false,
+          facebookId: "",
+          createdAt: new Date(),
+          updatedAt: new Date(),
+        });
+      }
+    }
+ 
+    // Facebook Authentication
+    else if (facebookToken) {
+      const response = await axios.get<{
+        id: string;
+        name: string;
+        email: string;
+        picture?: { data: { url: string } };
+      }>(
+        `https://graph.facebook.com/me?access_token=${facebookToken}&fields=id,name,email,picture`,
+      );
+ 
+      const { id: facebookId, name, email } = response.data;
+ 
+      if (!email) {
+        return res
+          .status(400)
+          .json({ message: "Facebook account must have an email" });
+      }
+ 
+      const [firstName] = name.split(" ");
+      const lastName = name.split(" ")[1] || "";
+ 
+      user = await User.findOne({ where: { email } });
+ 
+      if (user) {
+        if (!user.facebookId) {
+          user.facebookId = facebookId;
+          await user.save();
+        } else if (user.facebookId !== facebookId) {
+          return res
+            .status(400)
+            .json({ message: "Please use your exact email ID to sign in" });
+        }
+      } else {
+        user = await User.create({
+          facebookId,
+          email,
+          firstName,
+          lastName,
+          roleId: role.id,
+          country: "",
+          address: "",
+          shipAddress: [],
+          googleId: "",
+          imageUrl: response.data.picture?.data.url || "",
+          vip: false,
+          verifiedUser: false,
+          phoneNum: 0,
+          createdAt: new Date(),
+          updatedAt: new Date(),
+        });
+      }
+    }
+ 
+    if (!user) {
+      return res
+        .status(500)
+        .json({ message: "User creation failed. Please try again." });
+    }
+ 
+    // Generate JWT Token
+    const token = jwt.sign(
+      { UserId: user.id, email: user.email, roleId: user.roleId },
+      process.env.JWT_SECRET as string,
+      { expiresIn: "1h" },
+    );
+ 
+    return res.status(200).json({
+      message: "User logged in successfully",
+      token,
+      user,
+    });
+  } catch (error) {
+    next(error);
+  }
+};
+ 
+// send new OTP
+export const sendNewOTP = async (
+  req: Request<unknown, unknown, SendOtpRequest>,
+  res: Response,
+  next: NextFunction,
+) => {
+  const { email } = req.body;
+  if (!email) {
+    return res.status(400).json({ message: "Email is required" });
+  }
+ 
+  try {
+    const user = await User.findOne({ where: { email } });
+    if (!user) {
+      return res.status(400).json({
+        message:
+          "This email is not registered. Please enter the email with which you have registered your account!",
+      });
+    }
+ 
+    const code = generateOTP();
+ 
+    // Send OTP message
+    await sendOTP(email, code, user.phoneNum?.toString() || "");
+ 
+    // Save OTP to the database
+    const hashedOtp = hashSync(code, 10);
+    const otpObj = await UserOTPCode.create({
+      userId: user.id,
+      otp: hashedOtp,
+      expiredAt: new Date(Date.now() + 10 * 60 * 1000), // 10 minutes expiry
+      createdAt: new Date(),
+      updatedAt: new Date(),
+    });
+ 
+    if (!otpObj) {
+      throw new AppError("Failed to create OTP in the database", 500);
+    }
+ 
+    // Return result
+    return res.status(200).json({
+      message: "OTP sent successfully, please verify the OTP",
+      userID: user.id,
+      email: user.email,
+    });
+  } catch (error) {
+    if (error instanceof AppError) {
+      return res.status(error.statusCode).json({ message: error.message });
+    }
+    next(error);
+  }
+};
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/farming-product-REST-api/src/controllers/index.html b/coverage/lcov-report/farming-product-REST-api/src/controllers/index.html new file mode 100644 index 0000000..4b7afcd --- /dev/null +++ b/coverage/lcov-report/farming-product-REST-api/src/controllers/index.html @@ -0,0 +1,236 @@ + + + + + + Code coverage report for farming-product-REST-api/src/controllers + + + + + + + + + +
+
+

All files farming-product-REST-api/src/controllers

+
+ +
+ 7.8% + Statements + 177/2269 +
+ + +
+ 45% + Branches + 9/20 +
+ + +
+ 4.87% + Functions + 2/41 +
+ + +
+ 7.8% + Lines + 177/2269 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
auth.controller.ts +
+
18.21%94/51650%9/1822.22%2/918.21%94/516
multerStorage.ts +
+
0%0/560%0/10%0/10%0/56
notification.controller.ts +
+
5.18%7/135100%0/00%0/45.18%7/135
order.controller.ts +
+
5.58%24/430100%0/00%0/75.58%24/430
payment.collection.controller.ts +
+
0%0/3000%0/10%0/10%0/300
product.controller.ts +
+
7.89%18/228100%0/00%0/67.89%18/228
product.search.controller.ts +
+
4.95%6/121100%0/00%0/14.95%6/121
review.controller.ts +
+
4.77%13/272100%0/00%0/54.77%13/272
user.controller.ts +
+
7.1%15/211100%0/00%0/77.1%15/211
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/farming-product-REST-api/src/controllers/multerStorage.ts.html b/coverage/lcov-report/farming-product-REST-api/src/controllers/multerStorage.ts.html new file mode 100644 index 0000000..847231f --- /dev/null +++ b/coverage/lcov-report/farming-product-REST-api/src/controllers/multerStorage.ts.html @@ -0,0 +1,310 @@ + + + + + + Code coverage report for farming-product-REST-api/src/controllers/multerStorage.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api/src/controllers multerStorage.ts

+
+ +
+ 0% + Statements + 0/56 +
+ + +
+ 0% + Branches + 0/1 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 0% + Lines + 0/56 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import multer, { StorageEngine, MulterError, Multer } from "multer";
+import path from "path";
+import { Request, Response, NextFunction } from "express";
+import AppError from "../errors/customErrors";
+ 
+// Define custom types for multer request and response
+type MulterRequest = Request & { file?: Express.Multer.File };
+type MulterResponse = Response;
+ 
+// Define the storage configuration
+const storage: StorageEngine = multer.diskStorage({
+  destination: (_req, _file, cb) => {
+    cb(null, "./src/public/images");
+  },
+  filename: (_req, file, cb) => {
+    const uniqueSuffix = Date.now() + "-" + Math.round(Math.random() * 1e9);
+    cb(
+      null,
+      file.fieldname + "-" + uniqueSuffix + path.extname(file.originalname),
+    );
+  },
+});
+ 
+// Create multer instance with configuration
+const upload: Multer = multer({
+  storage,
+  limits: {
+    fileSize: 5 * 1024 * 1024, // 5MB max file size
+  },
+  fileFilter: (_req, file, cb) => {
+    // Accept only image files
+    if (!file.mimetype.startsWith("image")) {
+      cb(new Error("Only image files are allowed"));
+      return;
+    }
+    cb(null, true);
+  },
+});
+ 
+// Factory function to create the upload middleware
+const uploadMiddleware = (fieldName: string) => {
+  const middleware = upload.single(fieldName);
+ 
+  return (
+    req: MulterRequest,
+    res: MulterResponse,
+    next: NextFunction,
+  ): void => {
+    // Type assertion is necessary here due to multer's type incompatibility
+    const multerReq = req as unknown as Request;
+    const multerRes = res as unknown as Response;
+    const multerMiddleware = middleware as unknown as (
+      req: Request,
+      res: Response,
+      callback: (err: unknown) => void,
+    ) => void;
+    multerMiddleware(multerReq, multerRes, (err: unknown) => {
+      if (err instanceof MulterError) {
+        if (err.code === "LIMIT_FILE_SIZE") {
+          next(new AppError("File size too large. Maximum size is 5MB", 400));
+          return;
+        }
+        next(new AppError(err.message, 400));
+        return;
+      }
+      if (err instanceof Error) {
+        next(new AppError(err.message, 400));
+        return;
+      }
+      next();
+    });
+  };
+};
+ 
+export default uploadMiddleware;
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/farming-product-REST-api/src/controllers/notification.controller.ts.html b/coverage/lcov-report/farming-product-REST-api/src/controllers/notification.controller.ts.html new file mode 100644 index 0000000..0566372 --- /dev/null +++ b/coverage/lcov-report/farming-product-REST-api/src/controllers/notification.controller.ts.html @@ -0,0 +1,637 @@ + + + + + + Code coverage report for farming-product-REST-api/src/controllers/notification.controller.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api/src/controllers notification.controller.ts

+
+ +
+ 5.18% + Statements + 7/135 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/4 +
+ + +
+ 5.18% + Lines + 7/135 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +1851x +1x +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import Notification from "../models/notifiation";
+import User from "../models/user";
+import { Request, Response, NextFunction } from "express";
+import AppError from "../errors/customErrors";
+ 
+interface ExpoMessage {
+  to: string;
+  sound: "default" | "custom";
+  title: string;
+  body: string;
+  data?: Record<string, unknown>;
+  priority?: "default" | "normal" | "high";
+}
+ 
+interface ExpoResponse {
+  data: {
+    status: "ok" | "error";
+    id: string;
+    message: string;
+  }[];
+}
+ 
+interface CreateNotificationRequest {
+  title: string;
+  message: string;
+}
+ 
+// Get all notifications for a user
+export const getNotification = async (
+  req: Request<{ userId: string }>,
+  res: Response,
+  next: NextFunction,
+): Promise<void> => {
+  const { userId } = req.params;
+ 
+  try {
+    const allNotifications = await Notification.findAndCountAll({
+      where: { userId },
+      order: [["createdAt", "DESC"]],
+      include: {
+        model: User,
+        attributes: ["id", "firstName", "lastName", "country", "verifiedUser"],
+      },
+    });
+ 
+    res.status(200).json({
+      notifications: allNotifications,
+    });
+  } catch (error) {
+    next(error);
+  }
+};
+ 
+// Create a new notification
+export const createNotification = async (
+  req: Request<{ userId: string }, unknown, CreateNotificationRequest>,
+  res: Response,
+  next: NextFunction,
+): Promise<void> => {
+  const { userId } = req.params;
+  const { title, message } = req.body;
+ 
+  try {
+    if (!title || !message) {
+      throw new AppError("Title and message are required", 400);
+    }
+ 
+    const notification = await Notification.create({
+      userId,
+      message,
+      title,
+      isRead: false,
+      createdAt: new Date(),
+      updatedAt: new Date(),
+    });
+ 
+    if (!notification) {
+      throw new AppError("Failed to create notification", 500);
+    }
+ 
+    res.status(200).json({
+      result: notification,
+    });
+  } catch (error) {
+    if (error instanceof AppError) {
+      res.status(error.statusCode).json({ message: error.message });
+      return;
+    }
+    next(error);
+  }
+};
+ 
+// Mark a notification as read
+export const markAsRead = async (
+  req: Request<{ id: string }>,
+  res: Response,
+  next: NextFunction,
+): Promise<void> => {
+  const { id: notificationId } = req.params;
+ 
+  try {
+    const [updatedCount] = await Notification.update(
+      { isRead: true, updatedAt: new Date() },
+      { where: { id: notificationId } },
+    );
+ 
+    if (updatedCount === 0) {
+      throw new AppError("Notification not found", 404);
+    }
+ 
+    res.status(200).json({
+      message: "Notification marked as read",
+    });
+  } catch (error) {
+    if (error instanceof AppError) {
+      res.status(error.statusCode).json({ message: error.message });
+      return;
+    }
+    next(error);
+  }
+};
+ 
+// Test Expo notification
+export const testExpoNotification = async (
+  req: Request<{ userId: string }>,
+  res: Response,
+  next: NextFunction,
+): Promise<void> => {
+  const { userId } = req.params;
+ 
+  try {
+    const userData = await User.findOne({ where: { id: userId } });
+    if (!userData) {
+      throw new AppError("User not found", 404);
+    }
+ 
+    if (!userData.expoPushToken) {
+      throw new AppError("User has no push token registered", 400);
+    }
+ 
+    const message: ExpoMessage = {
+      to: userData.expoPushToken,
+      sound: "default",
+      title: "Test Notification",
+      body: "A test notification",
+      priority: "high",
+    };
+ 
+    const response = await fetch("https://exp.host/--/api/v2/push/send", {
+      method: "POST",
+      headers: {
+        Accept: "application/json",
+        "Content-Type": "application/json",
+      },
+      body: JSON.stringify(message),
+    });
+ 
+    if (!response.ok) {
+      throw new AppError(
+        `Failed to send notification: ${response.statusText}`,
+        response.status,
+      );
+    }
+ 
+    const result = (await response.json()) as ExpoResponse;
+ 
+    // Check for errors in the response
+    const error = result.data?.find((item) => item.status === "error");
+    if (error) {
+      throw new AppError(`Expo notification error: ${error.message}`, 500);
+    }
+ 
+    res.status(200).json({
+      message: "Notification sent successfully",
+      result,
+    });
+  } catch (error) {
+    if (error instanceof AppError) {
+      res.status(error.statusCode).json({ message: error.message });
+      return;
+    }
+    next(error);
+  }
+};
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/farming-product-REST-api/src/controllers/order.controller.ts.html b/coverage/lcov-report/farming-product-REST-api/src/controllers/order.controller.ts.html new file mode 100644 index 0000000..d2f7093 --- /dev/null +++ b/coverage/lcov-report/farming-product-REST-api/src/controllers/order.controller.ts.html @@ -0,0 +1,1708 @@ + + + + + + Code coverage report for farming-product-REST-api/src/controllers/order.controller.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api/src/controllers order.controller.ts

+
+ +
+ 5.58% + Statements + 24/430 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/7 +
+ + +
+ 5.58% + Lines + 24/430 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +335 +336 +337 +338 +339 +340 +341 +342 +343 +344 +345 +346 +347 +348 +349 +350 +351 +352 +353 +354 +355 +356 +357 +358 +359 +360 +361 +362 +363 +364 +365 +366 +367 +368 +369 +370 +371 +372 +373 +374 +375 +376 +377 +378 +379 +380 +381 +382 +383 +384 +385 +386 +387 +388 +389 +390 +391 +392 +393 +394 +395 +396 +397 +398 +399 +400 +401 +402 +403 +404 +405 +406 +407 +408 +409 +410 +411 +412 +413 +414 +415 +416 +417 +418 +419 +420 +421 +422 +423 +424 +425 +426 +427 +428 +429 +430 +431 +432 +433 +434 +435 +436 +437 +438 +439 +440 +441 +442 +443 +444 +445 +446 +447 +448 +449 +450 +451 +452 +453 +454 +455 +456 +457 +458 +459 +460 +461 +462 +463 +464 +465 +466 +467 +468 +469 +470 +471 +472 +473 +474 +475 +476 +477 +478 +479 +480 +481 +482 +483 +484 +485 +486 +487 +488 +489 +490 +491 +492 +493 +494 +495 +496 +497 +498 +499 +500 +501 +502 +503 +504 +505 +506 +507 +508 +509 +510 +511 +512 +513 +514 +515 +516 +517 +518 +519 +520 +521 +522 +523 +524 +525 +526 +527 +528 +529 +530 +531 +532 +533 +534 +535 +536 +537 +538 +539 +540 +541 +5421x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +1x +1x +1x +1x +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import User from "../models/user";
+import BuyerReview from "../models/buyerreview";
+import Product from "../models/product";
+import Order from "../models/order";
+import Transaction from "../models/transaction";
+import sequelize from "../models";
+import { Op, WhereOptions } from "sequelize";
+import { v2 as cloudinary } from "cloudinary";
+import * as fs from "fs";
+import sendPushNotificationToUser from "../middleware/send-notification";
+import handleExpoResponse, {
+  ExpoResponse,
+} from "../middleware/handleExpoResponse";
+import { Request, Response, NextFunction } from "express";
+import AppError from "../errors/customErrors";
+ 
+// Type definitions
+interface OrderWhereClause {
+  sellerId?: string;
+  buyerId?: string;
+  status?: string | { [Op.like]: string };
+}
+ 
+interface ProductWhereClause {
+  productName?: { [Op.like]: string };
+}
+ 
+interface PushNotificationMessage {
+  title: string;
+  text: string;
+}
+ 
+interface NotificationMessage {
+  title: string;
+  message: string;
+}
+ 
+interface DispatchDetails {
+  dispatchedAt: Date;
+  method: string;
+  imageUrl?: string;
+}
+ 
+interface CreateOrderRequest {
+  amount: number;
+  shipAddress: string;
+  weight: string;
+  sellerId: string;
+}
+ 
+interface UpdateOrderRequest {
+  userId: string;
+}
+ 
+interface DispatchRequest {
+  method: string;
+  date: Date;
+}
+ 
+// Configure cloudinary
+cloudinary.config({
+  cloud_name: process.env.CLOUDINARY_CLOUD_NAME,
+  api_key: process.env.CLOUDINARY_API_KEY,
+  api_secret: process.env.CLOUDINARY_API_SECRET,
+});
+ 
+// Get order by ID
+export const getOrderById = async (
+  req: Request<{ orderId: string }>,
+  res: Response,
+): Promise<void> => {
+  try {
+    const orderData = await Order.findOne({
+      where: { id: req.params.orderId },
+      include: [
+        {
+          model: User,
+          as: "buyer",
+          attributes: [
+            "id",
+            "firstName",
+            "lastName",
+            "country",
+            "verifiedUser",
+          ],
+        },
+        {
+          model: User,
+          as: "seller",
+          attributes: [
+            "id",
+            "firstName",
+            "lastName",
+            "country",
+            "verifiedUser",
+          ],
+        },
+        {
+          model: Product,
+          include: [
+            {
+              model: User,
+              attributes: [
+                "id",
+                "firstName",
+                "lastName",
+                "country",
+                "verifiedUser",
+              ],
+            },
+            {
+              model: BuyerReview,
+              attributes: ["id", "comment", "rating"],
+              required: false,
+            },
+          ],
+        },
+      ],
+    });
+ 
+    if (!orderData) {
+      throw new AppError("Order not found", 404);
+    }
+ 
+    res.status(200).json({
+      status: "success",
+      order: orderData,
+    });
+  } catch (error) {
+    if (error instanceof AppError) {
+      res.status(error.statusCode).json({ message: error.message });
+      return;
+    }
+    res.status(500).json({
+      message: error instanceof Error ? error.message : "An error occurred",
+    });
+  }
+};
+ 
+// Get buyer orders
+export const getBuyerOrders = async (
+  req: Request<{ buyerId: string }, unknown, unknown, { orderStatus?: string }>,
+  res: Response,
+): Promise<void> => {
+  const { buyerId } = req.params;
+  const { orderStatus } = req.query;
+ 
+  const whereClause: OrderWhereClause = {
+    buyerId,
+  };
+ 
+  if (orderStatus && orderStatus.trim() !== "") {
+    whereClause.status = orderStatus;
+  }
+ 
+  try {
+    const buyerOrders = await Order.findAndCountAll({
+      where: whereClause as WhereOptions,
+      include: [
+        {
+          model: User,
+          as: "seller",
+          attributes: [
+            "id",
+            "firstName",
+            "lastName",
+            "country",
+            "verifiedUser",
+          ],
+        },
+        {
+          model: Product,
+          include: [
+            {
+              model: User,
+              attributes: [
+                "id",
+                "firstName",
+                "lastName",
+                "country",
+                "verifiedUser",
+              ],
+            },
+            {
+              model: BuyerReview,
+              attributes: ["id", "comment", "rating"],
+              required: false,
+            },
+          ],
+        },
+      ],
+    });
+ 
+    res.status(200).json({
+      status: "success",
+      ordersData: buyerOrders,
+    });
+  } catch (error) {
+    res.status(500).json({
+      message: error instanceof Error ? error.message : "An error occurred",
+    });
+  }
+};
+ 
+// Get seller orders
+export const getSellerOrders = async (
+  req: Request<
+    { sellerId: string },
+    unknown,
+    unknown,
+    { orderStatus?: string; productName?: string }
+  >,
+  res: Response,
+): Promise<void> => {
+  const { sellerId } = req.params;
+  const { orderStatus, productName } = req.query;
+ 
+  const whereClause: OrderWhereClause = {
+    sellerId,
+  };
+ 
+  const prodWhereClause: ProductWhereClause = {};
+ 
+  if (orderStatus && orderStatus.trim() !== "") {
+    whereClause.status = orderStatus;
+  }
+  if (productName && productName.trim() !== "") {
+    prodWhereClause.productName = {
+      [Op.like]: `%${productName}%`,
+    };
+  }
+ 
+  try {
+    const sellerOrders = await Order.findAndCountAll({
+      where: whereClause as WhereOptions,
+      include: [
+        {
+          model: User,
+          as: "buyer",
+          attributes: [
+            "id",
+            "firstName",
+            "lastName",
+            "country",
+            "verifiedUser",
+          ],
+        },
+        {
+          model: Product,
+          where: prodWhereClause as WhereOptions,
+          include: [
+            {
+              model: User,
+              attributes: [
+                "id",
+                "firstName",
+                "lastName",
+                "country",
+                "verifiedUser",
+              ],
+            },
+            {
+              model: BuyerReview,
+              attributes: ["id", "comment", "rating"],
+              required: false,
+            },
+          ],
+        },
+      ],
+    });
+ 
+    res.status(200).json({
+      status: "success",
+      ordersData: sellerOrders,
+    });
+  } catch (error) {
+    res.status(500).json({
+      message: error instanceof Error ? error.message : "An error occurred",
+    });
+  }
+};
+ 
+// Create order
+export const createOrder = async (
+  req: Request<{ productId: string }, unknown, CreateOrderRequest>,
+  res: Response,
+): Promise<void> => {
+  const transaction = await sequelize.transaction();
+ 
+  try {
+    const { amount, shipAddress, weight, sellerId } = req.body;
+ 
+    // Validate required fields
+    if (!amount || !shipAddress || !weight || !sellerId) {
+      throw new AppError("Missing required fields", 400);
+    }
+ 
+    // Validate the sellerId Before Inserting
+    const seller = await User.findOne({ where: { id: sellerId } });
+    if (!seller) {
+      throw new AppError("Invalid sellerId: Seller does not exist", 400);
+    }
+ 
+    const order = await Order.create(
+      {
+        amount,
+        shipAddress,
+        weight,
+        sellerId: seller.id,
+        prodId: req.params.productId,
+        buyerId: "3ff1ceec-9f0d-4952-9c6c-fe3973dd8fa1", // TODO: Replace with actual buyer ID from auth
+        status: "pending",
+        dispatched: false,
+        createdAt: new Date(),
+        updatedAt: new Date(),
+      },
+      { transaction },
+    );
+ 
+    await Transaction.create(
+      {
+        amount: order.amount,
+        orderId: order.id,
+        status: "pending",
+        createdAt: new Date(),
+        updatedAt: new Date(),
+      },
+      { transaction },
+    );
+ 
+    await transaction.commit();
+ 
+    res.status(200).json({
+      message:
+        "Order created successfully. Please proceed toward payment else order can not be processed further",
+      orderDetails: order,
+    });
+  } catch (error) {
+    await transaction.rollback();
+    if (error instanceof AppError) {
+      res.status(error.statusCode).json({ message: error.message });
+      return;
+    }
+    res.status(500).json({
+      message: error instanceof Error ? error.message : "An error occurred",
+    });
+  }
+};
+ 
+// Update order
+export const updateOrder = async (
+  req: Request<{ orderId: string }, unknown, UpdateOrderRequest>,
+  res: Response,
+): Promise<void> => {
+  try {
+    const txOrder = await Transaction.findOne({
+      where: { orderId: req.params.orderId },
+    });
+ 
+    if (!txOrder) {
+      throw new AppError("Transaction not found for this order", 404);
+    }
+ 
+    if (txOrder.status !== "completed") {
+      throw new AppError(
+        "This Order is not in Transaction. Please make payment first",
+        403,
+      );
+    }
+ 
+    await Order.update(
+      {
+        status: "processing",
+        updatedAt: new Date(),
+      },
+      {
+        where: { id: req.params.orderId },
+      },
+    );
+ 
+    const orderData = await Order.findByPk(req.params.orderId);
+    if (!orderData) {
+      throw new AppError("Order not found", 404);
+    }
+ 
+    const [sellerData, buyerData] = await Promise.all([
+      User.findByPk(orderData.sellerId),
+      User.findByPk(req.body.userId),
+    ]);
+ 
+    // Send notifications
+    if (sellerData?.expoPushToken) {
+      const notificationMessage: NotificationMessage = {
+        title: "Order Completed",
+        message: "Congratulations! Your Order has been marked as completed",
+      };
+ 
+      const pushMessage: PushNotificationMessage = {
+        title: notificationMessage.title,
+        text: notificationMessage.message,
+      };
+ 
+      const result = await sendPushNotificationToUser(
+        sellerData.expoPushToken,
+        pushMessage,
+      );
+ 
+      if (result && "status" in result) {
+        await handleExpoResponse(
+          result as ExpoResponse,
+          orderData.sellerId,
+          notificationMessage,
+        );
+      }
+    }
+ 
+    if (buyerData?.expoPushToken) {
+      const notificationMessage: NotificationMessage = {
+        title: "Order Completion",
+        message: "You have marked your order as completed",
+      };
+ 
+      const pushMessage: PushNotificationMessage = {
+        title: notificationMessage.title,
+        text: notificationMessage.message,
+      };
+ 
+      const result = await sendPushNotificationToUser(
+        buyerData.expoPushToken,
+        pushMessage,
+      );
+ 
+      if (result && "status" in result) {
+        await handleExpoResponse(
+          result as ExpoResponse,
+          req.body.userId,
+          notificationMessage,
+        );
+      }
+    }
+ 
+    res.status(200).json({ message: "Order Completed Successfully!" });
+  } catch (error) {
+    if (error instanceof AppError) {
+      res.status(error.statusCode).json({ message: error.message });
+      return;
+    }
+    res.status(500).json({
+      message: error instanceof Error ? error.message : "An error occurred",
+    });
+  }
+};
+ 
+// Get transaction
+export const getTransaction = async (
+  req: Request<{ orderId: string }>,
+  res: Response,
+): Promise<void> => {
+  try {
+    const transaction = await Transaction.findOne({
+      where: { orderId: req.params.orderId },
+    });
+ 
+    if (!transaction) {
+      throw new AppError("Transaction not found", 404);
+    }
+ 
+    res.status(200).json({
+      message: "Transaction Details",
+      details: transaction,
+    });
+  } catch (error) {
+    if (error instanceof AppError) {
+      res.status(error.statusCode).json({ message: error.message });
+      return;
+    }
+    res.status(500).json({
+      message: error instanceof Error ? error.message : "An error occurred",
+    });
+  }
+};
+ 
+// Update dispatch details
+export const updateDispatchDetails = async (
+  req: Request<{ orderId: string }, unknown, DispatchRequest>,
+  res: Response,
+  next: NextFunction,
+): Promise<void> => {
+  try {
+    const { orderId } = req.params;
+    const { method, date } = req.body;
+ 
+    if (!method || !date) {
+      throw new AppError("Method and date are required", 400);
+    }
+ 
+    const details: DispatchDetails = {
+      dispatchedAt: new Date(),
+      method,
+    };
+ 
+    if (req.file) {
+      const cloudinaryImageUpload = await cloudinary.uploader.upload(
+        req.file.path,
+        {
+          resource_type: "image",
+        },
+      );
+ 
+      details.imageUrl = cloudinaryImageUpload.secure_url;
+      fs.unlinkSync(req.file.path);
+    }
+ 
+    const [updatedCount] = await Order.update(
+      {
+        status: "dispatched",
+        dispatched: true,
+        dispatchDetails: details,
+        deliveryDate: date,
+        updatedAt: new Date(),
+      },
+      {
+        where: { id: orderId },
+      },
+    );
+ 
+    if (updatedCount === 0) {
+      throw new AppError("Order not found", 404);
+    }
+ 
+    res.status(200).json({
+      message: "Dispatch details updated successfully",
+    });
+  } catch (error) {
+    if (error instanceof AppError) {
+      res.status(error.statusCode).json({ message: error.message });
+      return;
+    }
+    next(error);
+  }
+};
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/farming-product-REST-api/src/controllers/payment.collection.controller.ts.html b/coverage/lcov-report/farming-product-REST-api/src/controllers/payment.collection.controller.ts.html new file mode 100644 index 0000000..2577da5 --- /dev/null +++ b/coverage/lcov-report/farming-product-REST-api/src/controllers/payment.collection.controller.ts.html @@ -0,0 +1,1228 @@ + + + + + + Code coverage report for farming-product-REST-api/src/controllers/payment.collection.controller.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api/src/controllers payment.collection.controller.ts

+
+ +
+ 0% + Statements + 0/300 +
+ + +
+ 0% + Branches + 0/1 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 0% + Lines + 0/300 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +335 +336 +337 +338 +339 +340 +341 +342 +343 +344 +345 +346 +347 +348 +349 +350 +351 +352 +353 +354 +355 +356 +357 +358 +359 +360 +361 +362 +363 +364 +365 +366 +367 +368 +369 +370 +371 +372 +373 +374 +375 +376 +377 +378 +379 +380 +381 +382  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import User from "../models/user";
+import Order from "../models/order";
+import Transaction from "../models/transaction";
+import axios, { AxiosError } from "axios";
+import { Request, Response } from "express";
+import sequelize from "../models";
+import sendPushNotificationToUser from "../middleware/send-notification";
+import handleExpoResponse, {
+  ExpoResponse,
+} from "../middleware/handleExpoResponse";
+import AppError from "../errors/customErrors";
+ 
+interface PaymentData {
+  meanCode: string;
+  amount: string;
+  currency: string;
+  orderNumber?: string;
+}
+ 
+interface AdwaResponse {
+  data: {
+    tokenCode?: string;
+    adpFootprint?: string;
+    status?: string;
+    message?: string;
+  };
+}
+ 
+interface PushNotificationMessage {
+  title: string;
+  text: string;
+}
+ 
+interface NotificationMessage {
+  title: string;
+  message: string;
+}
+ 
+interface CollectionWebhookRequest {
+  status: string;
+  footPrint: string;
+  orderNumber: string;
+  moyenPaiement: string;
+  amount: number;
+}
+ 
+// Payment gateway adwapay configuration
+const MERCHANT_KEY = process.env.ADWA_MERCHANT_KEY;
+const APPLICATION_KEY = process.env.ADWA_APPLICATION_KEY;
+const SUBSCRIPTION_KEY = process.env.ADWA_SUBSCRIPTION_KEY;
+const BaseURL_Adwa = process.env.ADWA_BASE_URL;
+ 
+if (!MERCHANT_KEY || !APPLICATION_KEY || !SUBSCRIPTION_KEY || !BaseURL_Adwa) {
+  throw new Error("Missing required Adwa payment configuration");
+}
+ 
+// Get authentication token
+const getAuthToken = async (): Promise<AdwaResponse> => {
+  try {
+    const data = JSON.stringify({
+      application: APPLICATION_KEY,
+    });
+ 
+    const config = {
+      method: "post",
+      url: `${BaseURL_Adwa}/getADPToken`,
+      headers: {
+        "Content-Type": "application/json",
+        Authorization: `Basic ${Buffer.from(MERCHANT_KEY + ":" + SUBSCRIPTION_KEY).toString("base64")}`,
+      },
+      data,
+    };
+ 
+    const response = await axios(config);
+    return response.data;
+  } catch (error) {
+    if (error instanceof AxiosError && error.response) {
+      throw new AppError(
+        `Failed to get auth token: ${error.response.data.message || error.message}`,
+        error.response.status,
+      );
+    }
+    throw new AppError("Failed to get auth token", 500);
+  }
+};
+ 
+// Payment initiation
+const paymentCollectRequest = async (
+  data: PaymentData,
+  token: string,
+): Promise<AdwaResponse> => {
+  try {
+    const config = {
+      method: "post",
+      url: `${BaseURL_Adwa}/requestToPay`,
+      headers: {
+        "AUTH-API-TOKEN": `Bearer ${token}`,
+        "AUTH-API-SUBSCRIPTION": SUBSCRIPTION_KEY,
+        "Content-Type": "application/json",
+      },
+      data: JSON.stringify(data),
+    };
+ 
+    const response = await axios(config);
+    return response.data;
+  } catch (error) {
+    if (error instanceof AxiosError && error.response) {
+      throw new AppError(
+        `Payment request failed: ${error.response.data.message || error.message}`,
+        error.response.status,
+      );
+    }
+    throw new AppError("Payment request failed", 500);
+  }
+};
+ 
+// Check payment status
+const chargeStatusCheck = async (
+  footPrint: string,
+  meanCode: string,
+  token: string,
+): Promise<AdwaResponse> => {
+  try {
+    const data = JSON.stringify({
+      adpFootprint: footPrint,
+      meanCode,
+    });
+ 
+    const config = {
+      method: "post",
+      url: `${BaseURL_Adwa}/paymentStatus`,
+      headers: {
+        "AUTH-API-TOKEN": `Bearer ${token}`,
+        "AUTH-API-SUBSCRIPTION": SUBSCRIPTION_KEY,
+        "Content-Type": "application/json",
+      },
+      data,
+    };
+ 
+    const response = await axios(config);
+    return response.data;
+  } catch (error) {
+    if (error instanceof AxiosError && error.response) {
+      throw new AppError(
+        `Failed to check payment status: ${error.response.data.message || error.message}`,
+        error.response.status,
+      );
+    }
+    throw new AppError("Failed to check payment status", 500);
+  }
+};
+ 
+// Mobile payment collection
+export const mobilePaymentCollection = async (
+  req: Request<{ orderId: string }, unknown, PaymentData>,
+  res: Response,
+): Promise<void> => {
+  const { orderId } = req.params;
+  const paymentData = req.body;
+  const transaction = await sequelize.transaction();
+ 
+  try {
+    const order = await Order.findByPk(orderId);
+    if (!order) {
+      throw new AppError("Order not found or not created", 404);
+    }
+ 
+    const userToken = await getAuthToken();
+    if (!userToken.data.tokenCode) {
+      throw new AppError(
+        "Unable to get the token from the payment service providers. Please try again",
+        403,
+      );
+    }
+ 
+    // Requesting payment initiation
+    paymentData.orderNumber = `order_${orderId}_${Date.now()}`;
+    const paymentRequest = await paymentCollectRequest(
+      paymentData,
+      userToken.data.tokenCode,
+    );
+ 
+    if (
+      paymentData.meanCode === "MASTERCARD" ||
+      paymentData.meanCode === "VISA"
+    ) {
+      res.json({ message: paymentRequest.data });
+      return;
+    }
+ 
+    setTimeout(async () => {
+      try {
+        const resOutput = await chargeStatusCheck(
+          paymentRequest.data.adpFootprint as string,
+          paymentData.meanCode,
+          userToken.data.tokenCode as string,
+        );
+ 
+        if (resOutput.data.status === "T") {
+          await Transaction.update(
+            {
+              amount: parseFloat(paymentData.amount),
+              status: "completed",
+              txMethod: paymentData.meanCode,
+              currency: paymentData.currency,
+              orderId,
+              txDetails: resOutput.data,
+              updatedAt: new Date(),
+            },
+            { where: { orderId }, transaction },
+          );
+ 
+          await transaction.commit();
+ 
+          res.status(200).json({
+            status: "success",
+            message: resOutput.data,
+          });
+        } else {
+          await transaction.rollback();
+          res.status(400).json({
+            response: resOutput,
+            message:
+              "Payment was not successfully processed from the end-user.",
+          });
+        }
+      } catch (error) {
+        await transaction.rollback();
+        if (error instanceof AppError) {
+          res.status(error.statusCode).json({ message: error.message });
+        } else {
+          res.status(500).json({
+            message:
+              error instanceof Error
+                ? error.message
+                : "An error occurred during payment processing",
+          });
+        }
+      }
+    }, 100000);
+  } catch (error) {
+    await transaction.rollback();
+    if (error instanceof AppError) {
+      res.status(error.statusCode).json({ message: error.message });
+    } else {
+      res.status(500).json({
+        message:
+          error instanceof Error
+            ? error.message
+            : "An error occurred during payment processing",
+      });
+    }
+  }
+};
+ 
+// Collection webhook response
+export const collectionResponseAdwa = async (
+  req: Request<unknown, unknown, CollectionWebhookRequest>,
+  res: Response,
+): Promise<void> => {
+  const { status, footPrint, orderNumber, moyenPaiement, amount } = req.body;
+  const transaction = await sequelize.transaction();
+ 
+  try {
+    if (status !== "T") {
+      throw new AppError("Payment validation failed", 400);
+    }
+ 
+    const authToken = await getAuthToken();
+    if (!authToken.data.tokenCode) {
+      throw new AppError("Failed to get authentication token", 500);
+    }
+ 
+    const checkResponse = await chargeStatusCheck(
+      footPrint,
+      moyenPaiement,
+      authToken.data.tokenCode,
+    );
+ 
+    if (checkResponse.data.status !== "T") {
+      throw new AppError("Payment validation failed", 400);
+    }
+ 
+    const order = await Order.findByPk(orderNumber);
+    if (!order) {
+      throw new AppError("Order not found", 404);
+    }
+ 
+    const [sellerData, buyerData] = await Promise.all([
+      User.findByPk(order.sellerId),
+      User.findByPk(order.buyerId),
+    ]);
+ 
+    await Promise.all([
+      Transaction.update(
+        {
+          amount,
+          status: "completed",
+          txMethod: moyenPaiement,
+          txDetails: checkResponse.data,
+          updatedAt: new Date(),
+        },
+        { where: { orderId: order.id }, transaction },
+      ),
+      Order.update(
+        {
+          status: "processing",
+          updatedAt: new Date(),
+        },
+        { where: { id: order.id }, transaction },
+      ),
+    ]);
+ 
+    // Send notifications
+    if (sellerData?.expoPushToken) {
+      const notificationMessage: NotificationMessage = {
+        title: "New Order",
+        message: "Congratulations! You have received a New Order.",
+      };
+ 
+      const pushMessage: PushNotificationMessage = {
+        title: notificationMessage.title,
+        text: notificationMessage.message,
+      };
+ 
+      const result = await sendPushNotificationToUser(
+        sellerData.expoPushToken,
+        pushMessage,
+      );
+ 
+      if (result && "status" in result) {
+        await handleExpoResponse(
+          result as ExpoResponse,
+          order.sellerId,
+          notificationMessage,
+        );
+      }
+    }
+ 
+    if (buyerData?.expoPushToken) {
+      const notificationMessage: NotificationMessage = {
+        title: "Payment Done",
+        message:
+          "Your Payment has been Successfully Made and Your order has started",
+      };
+ 
+      const pushMessage: PushNotificationMessage = {
+        title: notificationMessage.title,
+        text: notificationMessage.message,
+      };
+ 
+      const result = await sendPushNotificationToUser(
+        buyerData.expoPushToken,
+        pushMessage,
+      );
+ 
+      if (result && "status" in result) {
+        await handleExpoResponse(
+          result as ExpoResponse,
+          order.buyerId,
+          notificationMessage,
+        );
+      }
+    }
+ 
+    await transaction.commit();
+    res.status(200).json({ message: "Payment processed successfully" });
+  } catch (error) {
+    await transaction.rollback();
+    if (error instanceof AppError) {
+      res.status(error.statusCode).json({ message: error.message });
+    } else {
+      res.status(500).json({
+        message:
+          error instanceof Error
+            ? error.message
+            : "An error occurred during payment processing",
+      });
+    }
+  }
+};
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/farming-product-REST-api/src/controllers/product.controller.ts.html b/coverage/lcov-report/farming-product-REST-api/src/controllers/product.controller.ts.html new file mode 100644 index 0000000..b50b7f1 --- /dev/null +++ b/coverage/lcov-report/farming-product-REST-api/src/controllers/product.controller.ts.html @@ -0,0 +1,943 @@ + + + + + + Code coverage report for farming-product-REST-api/src/controllers/product.controller.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api/src/controllers product.controller.ts

+
+ +
+ 7.89% + Statements + 18/228 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/6 +
+ + +
+ 7.89% + Lines + 18/228 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +2871x +1x +1x +1x +1x +1x +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +1x +1x +1x +1x +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import * as fs from "fs";
+import User from "../models/user";
+import Role from "../models/role";
+import BuyerReview from "../models/buyerreview";
+import Product from "../models/product";
+import { v2 as cloudinary } from "cloudinary";
+import { Request, Response, NextFunction } from "express";
+import { AuthenticatedRequest } from "../middleware/auth-check";
+import AppError from "../errors/customErrors";
+ 
+interface CloudinaryResponse {
+  secure_url: string;
+}
+ 
+interface CreateProductRequest {
+  productName: string;
+  description: string;
+  price: number;
+  quantity: number;
+  category: string;
+  imageUrl?: string;
+}
+ 
+type UpdateProductRequest = Partial<CreateProductRequest>;
+ 
+// Configure cloudinary
+cloudinary.config({
+  cloud_name: process.env.CLOUDINARY_CLOUD_NAME,
+  api_key: process.env.CLOUDINARY_API_KEY,
+  api_secret: process.env.CLOUDINARY_API_SECRET,
+});
+ 
+// Get all products
+export const allProducts = async (
+  req: Request,
+  res: Response,
+): Promise<void> => {
+  try {
+    const products = await Product.findAndCountAll({
+      include: [
+        {
+          model: User,
+          attributes: [
+            "id",
+            "firstName",
+            "lastName",
+            "country",
+            "verifiedUser",
+          ],
+          include: [{ model: Role }],
+        },
+      ],
+    });
+ 
+    if (!products) {
+      throw new AppError("No products found", 404);
+    }
+ 
+    res.status(200).json({ products });
+  } catch (error) {
+    if (error instanceof AppError) {
+      res.status(error.statusCode).json({ message: error.message });
+      return;
+    }
+    res.status(500).json({
+      message:
+        error instanceof Error ? error.message : "Error getting products",
+    });
+  }
+};
+ 
+// Get a product by ID
+export const getProduct = async (
+  req: Request<{ productId: string }>,
+  res: Response,
+): Promise<void> => {
+  try {
+    const foundProduct = await Product.findOne({
+      where: { id: req.params.productId },
+      include: [
+        {
+          model: User,
+          attributes: [
+            "id",
+            "firstName",
+            "lastName",
+            "country",
+            "imageUrl",
+            "verifiedUser",
+          ],
+        },
+        {
+          model: BuyerReview,
+          attributes: ["id", "comment", "rating", "createdAt"],
+          required: false,
+          include: [
+            {
+              model: User,
+              attributes: [
+                "id",
+                "firstName",
+                "lastName",
+                "country",
+                "imageUrl",
+                "verifiedUser",
+              ],
+            },
+          ],
+        },
+      ],
+    });
+ 
+    if (!foundProduct) {
+      throw new AppError("Product not found", 404);
+    }
+ 
+    res.status(200).json({ product: foundProduct });
+  } catch (error) {
+    if (error instanceof AppError) {
+      res.status(error.statusCode).json({ message: error.message });
+      return;
+    }
+    res.status(500).json({
+      message: error instanceof Error ? error.message : "Error getting product",
+    });
+  }
+};
+ 
+// Get all products of a user
+export const userProducts = async (
+  req: Request<{ userId: string }>,
+  res: Response,
+): Promise<void> => {
+  try {
+    const userProducts = await Product.findAndCountAll({
+      where: { userId: req.params.userId },
+    });
+ 
+    if (!userProducts) {
+      throw new AppError("No products found for this user", 404);
+    }
+ 
+    res.status(200).json({ products: userProducts });
+  } catch (error) {
+    if (error instanceof AppError) {
+      res.status(error.statusCode).json({ message: error.message });
+      return;
+    }
+    res.status(500).json({
+      message:
+        error instanceof Error ? error.message : "Error getting user products",
+    });
+  }
+};
+ 
+// Create a new product
+export const createProduct = async (
+  req: AuthenticatedRequest & { body: CreateProductRequest },
+  res: Response,
+  next: NextFunction,
+): Promise<void> => {
+  try {
+    if (!req.userData || typeof req.userData === "string") {
+      throw new AppError("Invalid authentication token", 401);
+    }
+ 
+    const { productName, description, price, quantity, category } = req.body;
+ 
+    // Validate required fields
+    if (!productName || !description || !price || !quantity || !category) {
+      throw new AppError("Missing required fields", 400);
+    }
+ 
+    const productData = {
+      ...req.body,
+      userId: req.userData.UserId,
+      createdAt: new Date(),
+      updatedAt: new Date(),
+    };
+ 
+    if (req.file) {
+      const cloudinaryResponse = (await cloudinary.uploader.upload(
+        req.file.path,
+        { resource_type: "image" },
+      )) as CloudinaryResponse;
+ 
+      productData.imageUrl = cloudinaryResponse.secure_url;
+ 
+      // Remove the file from public directory
+      fs.unlinkSync(req.file.path);
+    }
+ 
+    const result = await Product.create(productData);
+ 
+    res.status(201).json({
+      message: "Product created successfully",
+      product: result,
+    });
+  } catch (error) {
+    if (error instanceof AppError) {
+      res.status(error.statusCode).json({ message: error.message });
+      return;
+    }
+    next(error);
+  }
+};
+ 
+// Update a product
+export const updateProduct = async (
+  req: Request<{ productId: string }, unknown, UpdateProductRequest>,
+  res: Response,
+): Promise<void> => {
+  try {
+    const product = await Product.findByPk(req.params.productId);
+    if (!product) {
+      throw new AppError("Product not found", 404);
+    }
+ 
+    if (req.file) {
+      const cloudinaryResponse = (await cloudinary.uploader.upload(
+        req.file.path,
+      )) as CloudinaryResponse;
+ 
+      req.body.imageUrl = cloudinaryResponse.secure_url;
+ 
+      // Remove the file from public directory
+      fs.unlinkSync(req.file.path);
+    }
+ 
+    const productData = {
+      ...req.body,
+      updatedAt: new Date(),
+    };
+ 
+    const [updatedCount] = await Product.update(productData, {
+      where: { id: req.params.productId },
+    });
+ 
+    if (updatedCount === 0) {
+      throw new AppError("Failed to update product", 500);
+    }
+ 
+    res.status(200).json({ message: "Product updated successfully" });
+  } catch (error) {
+    if (error instanceof AppError) {
+      res.status(error.statusCode).json({ message: error.message });
+      return;
+    }
+    res.status(500).json({
+      message:
+        error instanceof Error ? error.message : "Error updating product",
+    });
+  }
+};
+ 
+// Delete a product
+export const removeProduct = async (
+  req: Request<{ productId: string }>,
+  res: Response,
+): Promise<void> => {
+  try {
+    const product = await Product.findByPk(req.params.productId);
+    if (!product) {
+      throw new AppError("Product not found", 404);
+    }
+ 
+    const deletedCount = await Product.destroy({
+      where: { id: req.params.productId },
+    });
+ 
+    if (deletedCount === 0) {
+      throw new AppError("Failed to delete product", 500);
+    }
+ 
+    res.status(200).json({ message: "Product has been deleted successfully" });
+  } catch (error) {
+    if (error instanceof AppError) {
+      res.status(error.statusCode).json({ message: error.message });
+      return;
+    }
+    res.status(500).json({
+      message:
+        error instanceof Error ? error.message : "Error deleting product",
+    });
+  }
+};
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/farming-product-REST-api/src/controllers/product.search.controller.ts.html b/coverage/lcov-report/farming-product-REST-api/src/controllers/product.search.controller.ts.html new file mode 100644 index 0000000..6b4744d --- /dev/null +++ b/coverage/lcov-report/farming-product-REST-api/src/controllers/product.search.controller.ts.html @@ -0,0 +1,553 @@ + + + + + + Code coverage report for farming-product-REST-api/src/controllers/product.search.controller.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api/src/controllers product.search.controller.ts

+
+ +
+ 4.95% + Statements + 6/121 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 4.95% + Lines + 6/121 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +1571x +1x +1x +1x +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import User from "../models/user";
+import BuyerReview from "../models/buyerreview";
+import Product from "../models/product";
+import { Op, WhereOptions } from "sequelize";
+import { Request, Response } from "express";
+import AppError from "../errors/customErrors";
+ 
+interface ProductSearchWhereClause {
+  productName?: { [Op.like]: string };
+  productCat?: { [Op.like]: string };
+  price?: { [Op.between]: [number, number] };
+  wholeSale?: boolean;
+}
+ 
+interface ProductSearchQuery {
+  productName?: string;
+  productCat?: string;
+  minPrice?: string;
+  maxPrice?: string;
+  productRating?: string;
+  wholeSale?: string;
+  page?: string;
+  limit?: string;
+}
+ 
+// Get all products with search and filters
+export const getAllProductSearch = async (
+  req: Request<unknown, unknown, unknown, ProductSearchQuery>,
+  res: Response,
+): Promise<void> => {
+  try {
+    const {
+      productName,
+      productCat = "All",
+      minPrice,
+      maxPrice,
+      productRating,
+      wholeSale,
+      page = "1",
+      limit = "10",
+    } = req.query;
+ 
+    // Validate pagination parameters
+    const pageNum = parseInt(page);
+    const limitNum = parseInt(limit);
+    if (isNaN(pageNum) || pageNum < 1) {
+      throw new AppError("Invalid page number", 400);
+    }
+    if (isNaN(limitNum) || limitNum < 1 || limitNum > 100) {
+      throw new AppError("Invalid limit value. Must be between 1 and 100", 400);
+    }
+ 
+    const offset: number = (pageNum - 1) * limitNum;
+    const whereClause: ProductSearchWhereClause = {};
+ 
+    // Build where clause based on query parameters
+    if (productName?.trim()) {
+      whereClause.productName = {
+        [Op.like]: `%${productName}%`,
+      };
+    }
+ 
+    if (productCat && productCat !== "All") {
+      whereClause.productCat = {
+        [Op.like]: `%${productCat}%`,
+      };
+    }
+ 
+    // Validate and add price range
+    const priceMin = minPrice ? parseInt(minPrice) : undefined;
+    const priceMax = maxPrice ? parseInt(maxPrice) : undefined;
+    if (priceMin !== undefined && priceMax !== undefined) {
+      if (isNaN(priceMin) || isNaN(priceMax)) {
+        throw new AppError("Invalid price range values", 400);
+      }
+      if (priceMin > priceMax) {
+        throw new AppError(
+          "Minimum price cannot be greater than maximum price",
+          400,
+        );
+      }
+      whereClause.price = {
+        [Op.between]: [priceMin, priceMax],
+      };
+    }
+ 
+    if (wholeSale === "true") {
+      whereClause.wholeSale = true;
+    }
+ 
+    // Validate rating
+    const rating = productRating ? parseInt(productRating) : 5;
+    if (isNaN(rating) || rating < 1 || rating > 5) {
+      throw new AppError("Invalid rating value. Must be between 1 and 5", 400);
+    }
+ 
+    const result = await Product.findAndCountAll({
+      where: whereClause as WhereOptions,
+      limit: limitNum,
+      offset,
+      include: [
+        {
+          model: User,
+          attributes: [
+            "id",
+            "firstName",
+            "lastName",
+            "country",
+            "verifiedUser",
+          ],
+        },
+        {
+          model: BuyerReview,
+          attributes: ["id", "comment", "rating"],
+          where: {
+            rating,
+          },
+          include: [
+            {
+              model: User,
+              attributes: [
+                "id",
+                "firstName",
+                "lastName",
+                "country",
+                "verifiedUser",
+              ],
+            },
+          ],
+          required: false,
+        },
+      ],
+    });
+ 
+    if (!result.count) {
+      res.status(200).json({
+        message: "No products found matching the search criteria",
+        queryResult: result,
+      });
+      return;
+    }
+ 
+    res.status(200).json({
+      queryResult: result,
+    });
+  } catch (error) {
+    if (error instanceof AppError) {
+      res.status(error.statusCode).json({ message: error.message });
+      return;
+    }
+    res.status(500).json({
+      message:
+        error instanceof Error ? error.message : "Error searching products",
+    });
+  }
+};
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/farming-product-REST-api/src/controllers/review.controller.ts.html b/coverage/lcov-report/farming-product-REST-api/src/controllers/review.controller.ts.html new file mode 100644 index 0000000..6708642 --- /dev/null +++ b/coverage/lcov-report/farming-product-REST-api/src/controllers/review.controller.ts.html @@ -0,0 +1,1153 @@ + + + + + + Code coverage report for farming-product-REST-api/src/controllers/review.controller.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api/src/controllers review.controller.ts

+
+ +
+ 4.77% + Statements + 13/272 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/5 +
+ + +
+ 4.77% + Lines + 13/272 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +335 +336 +337 +338 +339 +340 +341 +342 +343 +344 +345 +346 +347 +348 +349 +350 +351 +352 +353 +354 +355 +356 +3571x +1x +1x +1x +1x +  +1x +1x +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import User from "../models/user";
+import BuyerReview from "../models/buyerreview";
+import Product from "../models/product";
+import Order from "../models/order";
+import { Op, WhereOptions } from "sequelize";
+import { Request, Response } from "express";
+import sendPushNotificationToUser from "../middleware/send-notification";
+import handleExpoResponse, {
+  ExpoResponse,
+} from "../middleware/handleExpoResponse";
+import { AuthenticatedRequest } from "../middleware/auth-check";
+import AppError from "../errors/customErrors";
+ 
+interface ReviewWhereClause {
+  prodId: string;
+  rating?: { [Op.eq]: number };
+}
+ 
+interface PushNotificationMessage {
+  title: string;
+  text: string;
+}
+ 
+interface NotificationMessage {
+  title: string;
+  message: string;
+}
+ 
+interface CreateReviewRequest {
+  rating: number;
+  comment: string;
+}
+ 
+type UpdateReviewRequest = Partial<CreateReviewRequest>;
+ 
+// Get review of an order
+const orderReview = async (
+  req: Request<{ orderId: string }>,
+  res: Response,
+): Promise<void> => {
+  try {
+    const orderReview = await BuyerReview.findOne({
+      where: { orderId: req.params.orderId },
+      include: [
+        {
+          model: User,
+          attributes: [
+            "id",
+            "firstName",
+            "lastName",
+            "country",
+            "verifiedUser",
+          ],
+        },
+        {
+          model: Product,
+          attributes: ["id", "productName", "productCat", "imageUrl"],
+        },
+      ],
+    });
+ 
+    if (!orderReview) {
+      throw new AppError("Review not found for this order", 404);
+    }
+ 
+    res.status(200).json({
+      status: "success",
+      orderReviewData: orderReview,
+    });
+  } catch (error) {
+    if (error instanceof AppError) {
+      res.status(error.statusCode).json({ message: error.message });
+      return;
+    }
+    res.status(500).json({
+      message:
+        error instanceof Error
+          ? error.message
+          : "Error retrieving order review",
+    });
+  }
+};
+ 
+// Get reviews of a product
+const getReviewByProdId = async (
+  req: Request<{ productId: string }, unknown, unknown, { rating?: string }>,
+  res: Response,
+): Promise<void> => {
+  try {
+    const whereClause: ReviewWhereClause = {
+      prodId: req.params.productId,
+    };
+ 
+    const { rating } = req.query;
+    if (rating?.trim()) {
+      const ratingNum = Number(rating);
+      if (isNaN(ratingNum) || ratingNum < 1 || ratingNum > 5) {
+        throw new AppError(
+          "Invalid rating value. Must be between 1 and 5",
+          400,
+        );
+      }
+      whereClause.rating = {
+        [Op.eq]: ratingNum,
+      };
+    }
+ 
+    const reviews = await BuyerReview.findAndCountAll({
+      where: whereClause as unknown as WhereOptions,
+      include: [
+        {
+          model: User,
+          attributes: [
+            "id",
+            "firstName",
+            "lastName",
+            "country",
+            "verifiedUser",
+          ],
+        },
+        {
+          model: Product,
+          attributes: ["id", "productName", "productCat", "imageUrl"],
+        },
+      ],
+    });
+ 
+    if (!reviews.count) {
+      res.status(200).json({
+        message: "No reviews found for this product",
+        reviews,
+      });
+      return;
+    }
+ 
+    res.status(200).json({ reviews });
+  } catch (error) {
+    if (error instanceof AppError) {
+      res.status(error.statusCode).json({ message: error.message });
+      return;
+    }
+    res.status(500).json({
+      message:
+        error instanceof Error
+          ? error.message
+          : "Error retrieving product reviews",
+    });
+  }
+};
+ 
+// Create a review
+const createReview = async (
+  req: AuthenticatedRequest & {
+    body: CreateReviewRequest;
+    params: { productId: string; orderId: string };
+  },
+  res: Response,
+): Promise<void> => {
+  try {
+    if (!req.userData || typeof req.userData === "string") {
+      throw new AppError("Invalid authentication token", 401);
+    }
+ 
+    const { rating, comment } = req.body;
+ 
+    // Validate rating
+    if (!rating || rating < 1 || rating > 5) {
+      throw new AppError("Rating must be between 1 and 5", 400);
+    }
+ 
+    // Validate comment
+    if (!comment?.trim()) {
+      throw new AppError("Comment is required", 400);
+    }
+ 
+    const order = await Order.findOne({ where: { id: req.params.orderId } });
+    if (!order) {
+      throw new AppError("Order not found", 404);
+    }
+ 
+    if (order.status !== "delivered") {
+      throw new AppError(
+        "The order is still in processing or pending state. You cannot review yet",
+        401,
+      );
+    }
+ 
+    const reviewData = {
+      prodId: req.params.productId,
+      userId: req.userData.UserId,
+      orderId: req.params.orderId,
+      rating,
+      comment,
+      createdAt: new Date(),
+      updatedAt: new Date(),
+    };
+ 
+    const reviewOrder = { rating, comment };
+ 
+    await BuyerReview.create(reviewData);
+ 
+    await Order.update(
+      {
+        review: reviewOrder,
+        updatedAt: new Date(),
+      },
+      { where: { id: req.params.orderId } },
+    );
+ 
+    const sellerData = await User.findByPk(order.sellerId);
+ 
+    if (sellerData?.expoPushToken) {
+      const notificationMessage: NotificationMessage = {
+        title: "Order Reviewed",
+        message: "You got a review on your order from the buyer",
+      };
+ 
+      const pushMessage: PushNotificationMessage = {
+        title: notificationMessage.title,
+        text: notificationMessage.message,
+      };
+ 
+      const result = await sendPushNotificationToUser(
+        sellerData.expoPushToken,
+        pushMessage,
+      );
+ 
+      if (result && "status" in result) {
+        await handleExpoResponse(
+          result as ExpoResponse,
+          order.sellerId,
+          notificationMessage,
+        );
+      }
+    }
+ 
+    res.status(201).json({ message: "Review added successfully" });
+  } catch (error) {
+    if (error instanceof AppError) {
+      res.status(error.statusCode).json({ message: error.message });
+      return;
+    }
+    res.status(500).json({
+      message: error instanceof Error ? error.message : "Error creating review",
+    });
+  }
+};
+ 
+// Update a review
+const updateReview = async (
+  req: Request<{ reviewId: string }, unknown, UpdateReviewRequest>,
+  res: Response,
+): Promise<void> => {
+  try {
+    const { rating, comment } = req.body;
+ 
+    // Validate rating if provided
+    if (rating !== undefined && (rating < 1 || rating > 5)) {
+      throw new AppError("Rating must be between 1 and 5", 400);
+    }
+ 
+    // Validate comment if provided
+    if (comment !== undefined && !comment.trim()) {
+      throw new AppError("Comment cannot be empty", 400);
+    }
+ 
+    const review = await BuyerReview.findByPk(req.params.reviewId);
+    if (!review) {
+      throw new AppError("Review not found", 404);
+    }
+ 
+    const [updatedCount] = await BuyerReview.update(
+      {
+        ...req.body,
+        updatedAt: new Date(),
+      },
+      { where: { id: req.params.reviewId } },
+    );
+ 
+    if (updatedCount === 0) {
+      throw new AppError("Failed to update review", 500);
+    }
+ 
+    res.status(200).json({ message: "Review updated successfully" });
+  } catch (error) {
+    if (error instanceof AppError) {
+      res.status(error.statusCode).json({ message: error.message });
+      return;
+    }
+    res.status(500).json({
+      message: error instanceof Error ? error.message : "Error updating review",
+    });
+  }
+};
+ 
+// Delete own review
+const deleteOwnReview = async (
+  req: AuthenticatedRequest & { params: { reviewId: string } },
+  res: Response,
+): Promise<void> => {
+  try {
+    if (!req.userData || typeof req.userData === "string") {
+      throw new AppError("Invalid authentication token", 401);
+    }
+ 
+    const review = await BuyerReview.findOne({
+      where: { id: req.params.reviewId },
+      include: [
+        {
+          model: User,
+          attributes: [
+            "id",
+            "firstName",
+            "lastName",
+            "country",
+            "verifiedUser",
+          ],
+        },
+      ],
+    });
+ 
+    if (!review) {
+      throw new AppError("Review not found", 404);
+    }
+ 
+    if (review.userId !== req.userData.UserId) {
+      throw new AppError("You are not authorized to delete this review", 403);
+    }
+ 
+    const deletedCount = await BuyerReview.destroy({
+      where: { id: req.params.reviewId },
+    });
+ 
+    if (deletedCount === 0) {
+      throw new AppError("Failed to delete review", 500);
+    }
+ 
+    res.status(204).end();
+  } catch (error) {
+    if (error instanceof AppError) {
+      res.status(error.statusCode).json({ message: error.message });
+      return;
+    }
+    res.status(500).json({
+      message: error instanceof Error ? error.message : "Error deleting review",
+    });
+  }
+};
+ 
+export {
+  getReviewByProdId,
+  createReview,
+  updateReview,
+  deleteOwnReview,
+  orderReview,
+};
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/farming-product-REST-api/src/controllers/user.controller.ts.html b/coverage/lcov-report/farming-product-REST-api/src/controllers/user.controller.ts.html new file mode 100644 index 0000000..0808ac5 --- /dev/null +++ b/coverage/lcov-report/farming-product-REST-api/src/controllers/user.controller.ts.html @@ -0,0 +1,892 @@ + + + + + + Code coverage report for farming-product-REST-api/src/controllers/user.controller.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api/src/controllers user.controller.ts

+
+ +
+ 7.1% + Statements + 15/211 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/7 +
+ + +
+ 7.1% + Lines + 15/211 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +2701x +1x +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +1x +1x +1x +1x +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import User from "../models/user";
+import { hashSync, compare } from "bcryptjs";
+import { v2 as cloudinary } from "cloudinary";
+import { Request, Response, NextFunction } from "express";
+import { AuthenticatedRequest } from "../middleware/auth-check";
+ 
+interface CloudinaryResponse {
+  secure_url: string;
+}
+ 
+interface ShipAddress {
+  id: string;
+  title: string;
+  address: string;
+  default: boolean;
+}
+ 
+cloudinary.config({
+  cloud_name: process.env.CLOUDINARY_CLOUD_NAME,
+  api_key: process.env.CLOUDINARY_API_KEY,
+  api_secret: process.env.CLOUDINARY_API_SECRET,
+});
+ 
+// get all user data
+export const getAllUserData = async (
+  req: Request,
+  res: Response,
+): Promise<void> => {
+  try {
+    const users = await User.findAll();
+    if (!users?.length) {
+      res.status(400).json({ message: "No users found" });
+      return;
+    }
+ 
+    // remove password field from output
+    const usersWithoutPassword = users.map((user) => {
+      // eslint-disable-next-line @typescript-eslint/no-unused-vars
+      const { password, ...userData } = user.toJSON();
+      return userData;
+    });
+ 
+    res.status(200).json(usersWithoutPassword);
+  } catch (error) {
+    res.status(500).json({
+      message:
+        error instanceof Error ? error.message : "Error retrieving users",
+    });
+  }
+};
+ 
+// get a user data
+export const getUserData = async (
+  req: Request,
+  res: Response,
+): Promise<void> => {
+  const id = req.params.userId;
+ 
+  try {
+    const user = await User.findOne({ where: { id } });
+    if (!user) {
+      res.status(401).json({ message: "No such user found" });
+      return;
+    }
+ 
+    // eslint-disable-next-line @typescript-eslint/no-unused-vars
+    const { password, ...userData } = user.toJSON();
+ 
+    res.status(200).json(userData);
+  } catch (error) {
+    res.status(500).json({
+      message: error instanceof Error ? error.message : "Error retrieving user",
+    });
+  }
+};
+ 
+// Update a user's data
+export const updateUser = async (
+  req: AuthenticatedRequest,
+  res: Response,
+  next: NextFunction,
+): Promise<void> => {
+  const userId = req.params.userId;
+ 
+  try {
+    const user = await User.findByPk(userId);
+    if (!user) {
+      res.status(404).json({ message: "User not found" });
+      return;
+    }
+ 
+    if (req.file) {
+      const cloudinaryResponse = (await cloudinary.uploader.upload(
+        req.file.path,
+      )) as CloudinaryResponse;
+      req.body.imageUrl = cloudinaryResponse.secure_url;
+    }
+ 
+    const updatedUserData = {
+      ...req.body,
+      updatedAt: new Date(),
+    };
+ 
+    if (req.body.address && req.body.addressID) {
+      let shipAddress: ShipAddress[] = [];
+      const currentShipAddress =
+        typeof user.shipAddress === "string"
+          ? JSON.parse(user.shipAddress || "[]")
+          : (user.shipAddress as ShipAddress[]) || [];
+ 
+      shipAddress = Array.isArray(currentShipAddress) ? currentShipAddress : [];
+ 
+      const addressIndex = shipAddress.findIndex(
+        (addr) => addr.id === req.body.addressID,
+      );
+ 
+      if (addressIndex !== -1) {
+        shipAddress[addressIndex].address = req.body.address;
+        updatedUserData.shipAddress = shipAddress;
+      }
+    }
+ 
+    if (updatedUserData.password) {
+      updatedUserData.password = hashSync(updatedUserData.password, 10);
+    }
+ 
+    await User.update(updatedUserData, { where: { id: userId } });
+ 
+    const updatedUser = await User.findByPk(userId, {
+      attributes: { exclude: ["password"] },
+    });
+ 
+    res.status(200).json({
+      message: "Profile updated successfully",
+      userData: updatedUser,
+    });
+  } catch (error) {
+    next(error);
+  }
+};
+ 
+// delete a user
+export const deleteUser = async (
+  req: Request,
+  res: Response,
+): Promise<void> => {
+  try {
+    const userId = req.params.userId;
+ 
+    const existingUser = await User.findOne({ where: { id: userId } });
+    if (!existingUser) {
+      res.status(404).json({ message: "No such user found" });
+      return;
+    }
+ 
+    await User.destroy({ where: { id: userId } });
+    res.status(200).json({ message: "User deleted successfully" });
+  } catch (error) {
+    res.status(500).json({
+      message: error instanceof Error ? error.message : "Error deleting user",
+    });
+  }
+};
+ 
+// update password
+export const updatePassword = async (
+  req: Request,
+  res: Response,
+  next: NextFunction,
+): Promise<void> => {
+  const { password, userId, oldPassword } = req.body;
+ 
+  if (!password) {
+    res.status(400).json({
+      status: "FAILED",
+      message: "Empty input fields",
+    });
+    return;
+  }
+ 
+  if (password.length < 8) {
+    res.status(400).json({
+      status: "FAILED",
+      message: "Password must be at least 8 characters",
+    });
+    return;
+  }
+ 
+  try {
+    const userData = await User.findOne({ where: { id: userId } });
+    if (!userData) {
+      res.status(404).json({ message: "User not found" });
+      return;
+    }
+ 
+    if (oldPassword) {
+      const verifyPassword = await compare(
+        oldPassword,
+        userData.password as string,
+      );
+      if (!verifyPassword) {
+        res.status(403).json({
+          message:
+            "Current Password is incorrect. Please enter the correct current password",
+        });
+        return;
+      }
+    }
+ 
+    const hashedPassword = hashSync(password, 10);
+    userData.password = hashedPassword;
+    userData.updatedAt = new Date();
+ 
+    await User.update(userData, { where: { id: userId } });
+    res.status(200).json({ message: "Password successfully updated" });
+  } catch (error) {
+    next(error);
+  }
+};
+ 
+export const updateShipAddress = async (
+  req: Request,
+  res: Response,
+  next: NextFunction,
+): Promise<void> => {
+  const { userId } = req.params;
+ 
+  try {
+    await User.update(
+      {
+        shipAddress: req.body,
+        updatedAt: new Date(),
+      },
+      { where: { id: userId } },
+    );
+ 
+    const userData = await User.findOne({ where: { id: userId } });
+ 
+    res.status(200).json({
+      message: "Shipping address updated successfully",
+      data: userData,
+    });
+  } catch (error) {
+    next(error);
+  }
+};
+ 
+export const addExpoPushNotificationToken = async (
+  req: Request,
+  res: Response,
+  next: NextFunction,
+): Promise<void> => {
+  const { userId } = req.params;
+  const { expoPushToken } = req.body;
+ 
+  try {
+    await User.update(
+      {
+        expoPushToken,
+        updatedAt: new Date(),
+      },
+      { where: { id: userId } },
+    );
+ 
+    res.status(200).json({ message: "Push token saved successfully" });
+  } catch (error) {
+    next(error);
+  }
+};
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/farming-product-REST-api/src/errors/customErrors.ts.html b/coverage/lcov-report/farming-product-REST-api/src/errors/customErrors.ts.html new file mode 100644 index 0000000..549289a --- /dev/null +++ b/coverage/lcov-report/farming-product-REST-api/src/errors/customErrors.ts.html @@ -0,0 +1,136 @@ + + + + + + Code coverage report for farming-product-REST-api/src/errors/customErrors.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api/src/errors customErrors.ts

+
+ +
+ 100% + Statements + 10/10 +
+ + +
+ 100% + Branches + 3/3 +
+ + +
+ 100% + Functions + 1/1 +
+ + +
+ 100% + Lines + 10/10 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +181x +  +  +  +  +1x +5x +  +5x +5x +5x +  +5x +5x +1x +  +1x + 
class AppError extends Error {
+  public statusCode: number;
+  public status: string;
+  public isOperational: boolean;
+ 
+  constructor(message: string, statusCode: number) {
+    super(message);
+ 
+    this.statusCode = statusCode;
+    this.status = `${statusCode}`.startsWith("4") ? "fail" : "error";
+    this.isOperational = true;
+ 
+    Error.captureStackTrace(this, this.constructor);
+  }
+}
+ 
+export default AppError;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/farming-product-REST-api/src/errors/index.html b/coverage/lcov-report/farming-product-REST-api/src/errors/index.html new file mode 100644 index 0000000..e834dfe --- /dev/null +++ b/coverage/lcov-report/farming-product-REST-api/src/errors/index.html @@ -0,0 +1,116 @@ + + + + + + Code coverage report for farming-product-REST-api/src/errors + + + + + + + + + +
+
+

All files farming-product-REST-api/src/errors

+
+ +
+ 100% + Statements + 10/10 +
+ + +
+ 100% + Branches + 3/3 +
+ + +
+ 100% + Functions + 1/1 +
+ + +
+ 100% + Lines + 10/10 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
customErrors.ts +
+
100%10/10100%3/3100%1/1100%10/10
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/farming-product-REST-api/src/middleware/auth-check.ts.html b/coverage/lcov-report/farming-product-REST-api/src/middleware/auth-check.ts.html new file mode 100644 index 0000000..20e4c89 --- /dev/null +++ b/coverage/lcov-report/farming-product-REST-api/src/middleware/auth-check.ts.html @@ -0,0 +1,349 @@ + + + + + + Code coverage report for farming-product-REST-api/src/middleware/auth-check.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api/src/middleware auth-check.ts

+
+ +
+ 6.77% + Statements + 4/59 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 6.77% + Lines + 4/59 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89  +1x +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x + 
import { Request, Response, NextFunction } from "express";
+import jwt, {
+  JwtPayload,
+  JsonWebTokenError,
+  TokenExpiredError,
+} from "jsonwebtoken";
+import AppError from "../errors/customErrors";
+ 
+interface DecodedToken extends JwtPayload {
+  UserId: string;
+}
+ 
+export interface AuthenticatedRequest extends Request {
+  userData?: DecodedToken;
+}
+ 
+const authCheck = async (
+  req: AuthenticatedRequest,
+  res: Response,
+  next: NextFunction,
+): Promise<void> => {
+  try {
+    // Check if authorization header is present
+    const authHeader = req.headers.authorization;
+    if (!authHeader) {
+      res.status(401).json({
+        message: "You are either not logged in or your session has expired",
+      });
+      return;
+    }
+ 
+    // Extract the token from the authorization header
+    const [bearer, token] = authHeader.split(" ");
+    if (bearer !== "Bearer" || !token) {
+      res.status(401).json({
+        message: "Invalid authorization header format",
+      });
+      return;
+    }
+ 
+    // Verify the token
+    const decodedToken = jwt.verify(
+      token,
+      process.env.JWT_SECRET as string,
+    ) as DecodedToken;
+ 
+    // Check if the token is expired
+    if (decodedToken.exp && decodedToken.exp < Date.now() / 1000) {
+      res.status(401).json({
+        auth: false,
+        message: "Your session has expired",
+      });
+      return;
+    }
+ 
+    // Attach decoded token data to the request object
+    req.userData = decodedToken;
+ 
+    // Verify the userId parameter matches the token's user ID, if applicable
+    if (req.params.userId && req.params.userId !== decodedToken.UserId) {
+      res.status(403).json({
+        message:
+          "You are not authorized for this, please log in using your account",
+      });
+      return;
+    }
+ 
+    // Proceed to the next middleware or route handler
+    next();
+  } catch (error) {
+    if (error instanceof TokenExpiredError) {
+      res.status(401).json({
+        message: "Your session has expired",
+      });
+      return;
+    }
+    if (error instanceof JsonWebTokenError) {
+      res.status(401).json({
+        message: "Invalid token",
+      });
+      return;
+    }
+    // Handle unexpected errors
+    next(new AppError("Authentication error", 500));
+  }
+};
+ 
+export default authCheck;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/farming-product-REST-api/src/middleware/errorHandler.ts.html b/coverage/lcov-report/farming-product-REST-api/src/middleware/errorHandler.ts.html new file mode 100644 index 0000000..41ad42a --- /dev/null +++ b/coverage/lcov-report/farming-product-REST-api/src/middleware/errorHandler.ts.html @@ -0,0 +1,196 @@ + + + + + + Code coverage report for farming-product-REST-api/src/middleware/errorHandler.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api/src/middleware errorHandler.ts

+
+ +
+ 90.47% + Statements + 19/21 +
+ + +
+ 25% + Branches + 1/4 +
+ + +
+ 100% + Functions + 1/1 +
+ + +
+ 90.47% + Lines + 19/21 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38  +1x +  +  +  +  +  +  +  +1x +4x +4x +4x +4x +4x +4x +  +  +4x +4x +  +  +4x +4x +4x +4x +  +  +4x +  +  +  +4x +4x +4x +  +1x + 
import { Request, Response, NextFunction } from "express";
+import AppError from "../errors/customErrors";
+ 
+interface ErrorResponse {
+  status: string;
+  message: string;
+  stack?: string;
+}
+ 
+const errorHandler = (
+  err: Error | AppError,
+  _req: Request,
+  res: Response,
+  _next: NextFunction,
+): void => {
+  console.log("Error handler called:", err);
+ 
+  // Set default values
+  const statusCode = err instanceof AppError ? err.statusCode : 500;
+  const status = "fail"; // Always use 'fail' for consistency with tests
+ 
+  // Prepare error response
+  const errorResponse: ErrorResponse = {
+    status,
+    message: err.message || "An unexpected error occurred",
+  };
+ 
+  // Add stack trace in development environment
+  if (process.env.NODE_ENV === "development") {
+    errorResponse.stack = err.stack;
+  }
+ 
+  console.log("Sending error response:", errorResponse);
+  res.status(statusCode).json(errorResponse);
+};
+ 
+export default errorHandler;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/farming-product-REST-api/src/middleware/handleExpoResponse.ts.html b/coverage/lcov-report/farming-product-REST-api/src/middleware/handleExpoResponse.ts.html new file mode 100644 index 0000000..203219a --- /dev/null +++ b/coverage/lcov-report/farming-product-REST-api/src/middleware/handleExpoResponse.ts.html @@ -0,0 +1,340 @@ + + + + + + Code coverage report for farming-product-REST-api/src/middleware/handleExpoResponse.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api/src/middleware handleExpoResponse.ts

+
+ +
+ 10.86% + Statements + 5/46 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 10.86% + Lines + 5/46 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +861x +1x +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x + 
import Notification from "../models/notifiation";
+import User from "../models/user";
+import AppError from "../errors/customErrors";
+ 
+export interface ExpoResponse {
+  status: "ok" | "error";
+  details?: {
+    error?:
+      | "DeviceNotRegistered"
+      | "MessageTooBig"
+      | "InvalidCredentials"
+      | string;
+  };
+}
+ 
+interface MessageToSend {
+  title: string;
+  message: string;
+}
+ 
+interface NotificationData {
+  userId: string;
+  title: string;
+  message: string;
+  isRead: boolean;
+}
+ 
+/**
+ * Handles the response from Expo's push notification service
+ * @param result - The response from Expo's push notification service
+ * @param userId - The ID of the user to receive the notification
+ * @param messageToSend - The notification message details
+ * @throws {AppError} When there's an error creating the notification
+ */
+const expoNotificationResponse = async (
+  result: ExpoResponse,
+  userId: string,
+  messageToSend: MessageToSend,
+): Promise<void> => {
+  try {
+    if (result.status === "ok") {
+      const notificationData: NotificationData = {
+        userId,
+        title: messageToSend.title,
+        message: messageToSend.message,
+        isRead: false,
+      };
+ 
+      await Notification.create(notificationData);
+      return;
+    }
+ 
+    if (
+      result.status === "error" &&
+      result.details?.error === "DeviceNotRegistered"
+    ) {
+      // Remove the invalid push token from the user's record
+      const updateResult = await User.update(
+        { expoPushToken: null },
+        { where: { id: userId } },
+      );
+ 
+      // Check if the user was actually updated
+      if (updateResult[0] === 0) {
+        throw new AppError(`User not found with ID: ${userId}`, 404);
+      }
+      return;
+    }
+ 
+    // Handle other error cases
+    if (result.status === "error") {
+      throw new AppError(
+        `Expo notification error: ${result.details?.error || "Unknown error"}`,
+        500,
+      );
+    }
+  } catch (error) {
+    if (error instanceof AppError) {
+      throw error;
+    }
+    throw new AppError("Failed to process Expo notification response", 500);
+  }
+};
+ 
+export default expoNotificationResponse;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/farming-product-REST-api/src/middleware/index.html b/coverage/lcov-report/farming-product-REST-api/src/middleware/index.html new file mode 100644 index 0000000..b9ce518 --- /dev/null +++ b/coverage/lcov-report/farming-product-REST-api/src/middleware/index.html @@ -0,0 +1,191 @@ + + + + + + Code coverage report for farming-product-REST-api/src/middleware + + + + + + + + + +
+
+

All files farming-product-REST-api/src/middleware

+
+ +
+ 26.07% + Statements + 67/257 +
+ + +
+ 50% + Branches + 3/6 +
+ + +
+ 30% + Functions + 3/10 +
+ + +
+ 26.07% + Lines + 67/257 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
auth-check.ts +
+
6.77%4/59100%0/00%0/16.77%4/59
errorHandler.ts +
+
90.47%19/2125%1/4100%1/190.47%19/21
handleExpoResponse.ts +
+
10.86%5/46100%0/00%0/110.86%5/46
multerStorage.ts +
+
33.92%19/56100%1/125%1/433.92%19/56
rateLimiter.ts +
+
76.19%16/21100%1/150%1/276.19%16/21
send-notification.ts +
+
7.4%4/54100%0/00%0/17.4%4/54
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/farming-product-REST-api/src/middleware/multerStorage.ts.html b/coverage/lcov-report/farming-product-REST-api/src/middleware/multerStorage.ts.html new file mode 100644 index 0000000..b348b11 --- /dev/null +++ b/coverage/lcov-report/farming-product-REST-api/src/middleware/multerStorage.ts.html @@ -0,0 +1,310 @@ + + + + + + Code coverage report for farming-product-REST-api/src/middleware/multerStorage.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api/src/middleware multerStorage.ts

+
+ +
+ 33.92% + Statements + 19/56 +
+ + +
+ 100% + Branches + 1/1 +
+ + +
+ 25% + Functions + 1/4 +
+ + +
+ 33.92% + Lines + 19/56 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +761x +1x +  +1x +  +  +  +  +  +  +1x +1x +  +  +1x +  +  +  +  +  +  +1x +  +  +1x +1x +1x +1x +1x +1x +  +  +  +  +  +  +  +1x +  +  +1x +8x +  +8x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +8x +  +1x + 
import multer, { StorageEngine, MulterError, Multer } from "multer";
+import path from "path";
+import { Request, Response, NextFunction } from "express";
+import AppError from "../errors/customErrors";
+ 
+// Define custom types for multer request and response
+type MulterRequest = Request & { file?: Express.Multer.File };
+type MulterResponse = Response;
+ 
+// Define the storage configuration
+const storage: StorageEngine = multer.diskStorage({
+  destination: (_req, _file, cb) => {
+    cb(null, "./src/public/images");
+  },
+  filename: (_req, file, cb) => {
+    const uniqueSuffix = Date.now() + "-" + Math.round(Math.random() * 1e9);
+    cb(
+      null,
+      file.fieldname + "-" + uniqueSuffix + path.extname(file.originalname),
+    );
+  },
+});
+ 
+// Create multer instance with configuration
+const upload: Multer = multer({
+  storage,
+  limits: {
+    fileSize: 5 * 1024 * 1024, // 5MB max file size
+  },
+  fileFilter: (_req, file, cb) => {
+    // Accept only image files
+    if (!file.mimetype.startsWith("image")) {
+      cb(new Error("Only image files are allowed"));
+      return;
+    }
+    cb(null, true);
+  },
+});
+ 
+// Factory function to create the upload middleware
+const uploadMiddleware = (fieldName: string) => {
+  const middleware = upload.single(fieldName);
+ 
+  return (
+    req: MulterRequest,
+    res: MulterResponse,
+    next: NextFunction,
+  ): void => {
+    // Type assertion is necessary here due to multer's type incompatibility
+    const multerReq = req as unknown as Request;
+    const multerRes = res as unknown as Response;
+    const multerMiddleware = middleware as unknown as (
+      req: Request,
+      res: Response,
+      callback: (err: unknown) => void,
+    ) => void;
+    multerMiddleware(multerReq, multerRes, (err: unknown) => {
+      if (err instanceof MulterError) {
+        if (err.code === "LIMIT_FILE_SIZE") {
+          next(new AppError("File size too large. Maximum size is 5MB", 400));
+          return;
+        }
+        next(new AppError(err.message, 400));
+        return;
+      }
+      if (err instanceof Error) {
+        next(new AppError(err.message, 400));
+        return;
+      }
+      next();
+    });
+  };
+};
+ 
+export default uploadMiddleware;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/farming-product-REST-api/src/middleware/rateLimiter.ts.html b/coverage/lcov-report/farming-product-REST-api/src/middleware/rateLimiter.ts.html new file mode 100644 index 0000000..37be29c --- /dev/null +++ b/coverage/lcov-report/farming-product-REST-api/src/middleware/rateLimiter.ts.html @@ -0,0 +1,163 @@ + + + + + + Code coverage report for farming-product-REST-api/src/middleware/rateLimiter.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api/src/middleware rateLimiter.ts

+
+ +
+ 76.19% + Statements + 16/21 +
+ + +
+ 100% + Branches + 1/1 +
+ + +
+ 50% + Functions + 1/2 +
+ + +
+ 76.19% + Lines + 16/21 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +271x +1x +  +  +1x +1x +1x +1x +1x +1x +1x +1x +  +  +  +  +  +1x +  +4x +4x +1x +  +1x +  +1x + 
import { rateLimit, Options } from "express-rate-limit";
+import AppError from "../errors/customErrors";
+ 
+// Rate limiter configuration
+const rateLimitConfig: Partial<Options> = {
+  windowMs: 10 * 60 * 1000, // 10 minutes
+  max: 40, // Limit each IP to 40 requests per window
+  standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers
+  legacyHeaders: false, // Disable the `X-RateLimit-*` headers
+  message: "Too many requests from this IP, please try again later",
+  statusCode: 429, // Too Many Requests
+  handler: () => {
+    throw new AppError(
+      "Too many requests from this IP, please try again later",
+      429,
+    );
+  },
+  skip: (req) => {
+    // Skip rate limiting for health check endpoint
+    return req.path === "/health";
+  },
+};
+ 
+const limiter = rateLimit(rateLimitConfig);
+ 
+export default limiter;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/farming-product-REST-api/src/middleware/send-notification.ts.html b/coverage/lcov-report/farming-product-REST-api/src/middleware/send-notification.ts.html new file mode 100644 index 0000000..d4d2ab3 --- /dev/null +++ b/coverage/lcov-report/farming-product-REST-api/src/middleware/send-notification.ts.html @@ -0,0 +1,361 @@ + + + + + + Code coverage report for farming-product-REST-api/src/middleware/send-notification.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api/src/middleware send-notification.ts

+
+ +
+ 7.4% + Statements + 4/54 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 7.4% + Lines + 4/54 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +931x +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x + 
import fetch, { Response } from "node-fetch";
+import AppError from "../errors/customErrors";
+ 
+interface MessageToSend {
+  title: string;
+  text: string;
+}
+ 
+interface ExpoMessage {
+  to: string;
+  sound: "default" | "custom";
+  title: string;
+  body: string;
+  data?: Record<string, unknown>;
+  priority?: "default" | "normal" | "high";
+}
+ 
+interface ExpoResponse {
+  data: {
+    status: "ok" | "error";
+    id: string;
+    message: string;
+  }[];
+}
+ 
+/**
+ * Sends a push notification to a user via Expo's push notification service
+ * @param pushToken - The user's Expo push token
+ * @param messageToSend - The notification message details
+ * @returns The response from Expo's push notification service
+ * @throws {AppError} When the notification fails to send
+ */
+const sendPushNotificationToUser = async (
+  pushToken: string | null,
+  messageToSend: MessageToSend,
+): Promise<ExpoResponse | undefined> => {
+  if (!pushToken) {
+    return undefined;
+  }
+ 
+  const message: ExpoMessage = {
+    to: pushToken,
+    sound: "default",
+    title: messageToSend.title,
+    body: messageToSend.text,
+    priority: "high",
+  };
+ 
+  try {
+    const response: Response = await fetch(
+      "https://exp.host/--/api/v2/push/send",
+      {
+        method: "POST",
+        headers: {
+          Accept: "application/json",
+          "Content-Type": "application/json",
+        },
+        body: JSON.stringify(message),
+      },
+    );
+ 
+    if (!response.ok) {
+      throw new AppError(
+        `Failed to send notification: ${response.statusText}`,
+        response.status,
+      );
+    }
+ 
+    const result = (await response.json()) as ExpoResponse;
+ 
+    // Check for errors in the response
+    const error = result.data?.find((item) => item.status === "error");
+    if (error) {
+      throw new AppError(`Expo notification error: ${error.message}`, 500);
+    }
+ 
+    return result;
+  } catch (error) {
+    if (error instanceof AppError) {
+      throw error;
+    }
+    if (error instanceof Error) {
+      throw new AppError(
+        `Failed to send push notification: ${error.message}`,
+        500,
+      );
+    }
+    throw new AppError("Failed to send push notification", 500);
+  }
+};
+ 
+export default sendPushNotificationToUser;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/farming-product-REST-api/src/models/buyerreview.ts.html b/coverage/lcov-report/farming-product-REST-api/src/models/buyerreview.ts.html new file mode 100644 index 0000000..01da24a --- /dev/null +++ b/coverage/lcov-report/farming-product-REST-api/src/models/buyerreview.ts.html @@ -0,0 +1,298 @@ + + + + + + Code coverage report for farming-product-REST-api/src/models/buyerreview.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api/src/models buyerreview.ts

+
+ +
+ 100% + Statements + 52/52 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 100% + Functions + 0/0 +
+ + +
+ 100% + Lines + 52/52 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +721x +  +  +  +  +  +  +  +  +  +  +1x +1x +1x +  +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +  +1x +1x +1x +1x +1x +  +1x +1x +1x +1x +1x +  +1x +1x +1x +1x +1x +  +1x +1x +  +1x +1x +  +1x +1x +  +1x +1x +1x +1x +1x +  +1x + 
import {
+  Table,
+  Column,
+  Model,
+  DataType,
+  CreatedAt,
+  UpdatedAt,
+  ForeignKey,
+  BelongsTo,
+} from "sequelize-typescript";
+import { BuyerReviewAttributes } from "../types";
+import Product from "./product";
+import User from "./user";
+import Order from "./order";
+ 
+@Table({
+  timestamps: true,
+  tableName: "BuyerReviews",
+  modelName: "BuyerReview",
+})
+class BuyerReview extends Model<BuyerReviewAttributes> {
+  @Column({
+    primaryKey: true,
+    type: DataType.UUID,
+    defaultValue: DataType.UUIDV4,
+    allowNull: false,
+  })
+  declare id: string;
+  @Column({
+    type: DataType.STRING,
+  })
+  declare comment: string;
+  @Column({
+    type: DataType.INTEGER,
+  })
+  declare rating: number;
+ 
+  @ForeignKey(() => User)
+  @Column({
+    type: DataType.UUID,
+  })
+  userId!: string;
+ 
+  @ForeignKey(() => Product)
+  @Column({
+    type: DataType.UUID,
+  })
+  prodId!: string;
+ 
+  @ForeignKey(() => Order)
+  @Column({
+    type: DataType.UUID,
+  })
+  orderId!: string;
+ 
+  @BelongsTo(() => User)
+  user?: User;
+ 
+  @BelongsTo(() => Product)
+  product?: Product;
+ 
+  @BelongsTo(() => Order)
+  order?: Order;
+ 
+  @CreatedAt
+  declare createdAt: Date;
+  @UpdatedAt
+  declare updatedAt: Date;
+}
+ 
+export default BuyerReview;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/farming-product-REST-api/src/models/index.html b/coverage/lcov-report/farming-product-REST-api/src/models/index.html new file mode 100644 index 0000000..feebe71 --- /dev/null +++ b/coverage/lcov-report/farming-product-REST-api/src/models/index.html @@ -0,0 +1,236 @@ + + + + + + Code coverage report for farming-product-REST-api/src/models + + + + + + + + + +
+
+

All files farming-product-REST-api/src/models

+
+ +
+ 73.21% + Statements + 440/601 +
+ + +
+ 83.33% + Branches + 5/6 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 73.21% + Lines + 440/601 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
buyerreview.ts +
+
100%52/52100%0/0100%0/0100%52/52
index.ts +
+
0%0/1610%0/10%0/10%0/161
notifiation.ts +
+
100%41/41100%0/0100%0/0100%41/41
order.ts +
+
100%77/77100%0/0100%0/0100%77/77
product.ts +
+
100%62/62100%0/0100%0/0100%62/62
role.ts +
+
100%23/23100%0/0100%0/0100%23/23
transaction.ts +
+
100%52/52100%0/0100%0/0100%52/52
user.ts +
+
100%96/96100%5/5100%0/0100%96/96
userotpcode.ts +
+
100%37/37100%0/0100%0/0100%37/37
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/farming-product-REST-api/src/models/index.ts.html b/coverage/lcov-report/farming-product-REST-api/src/models/index.ts.html new file mode 100644 index 0000000..383f59f --- /dev/null +++ b/coverage/lcov-report/farming-product-REST-api/src/models/index.ts.html @@ -0,0 +1,826 @@ + + + + + + Code coverage report for farming-product-REST-api/src/models/index.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api/src/models index.ts

+
+ +
+ 0% + Statements + 0/161 +
+ + +
+ 0% + Branches + 0/1 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 0% + Lines + 0/161 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import dotenv from "dotenv";
+dotenv.config();
+import { join } from "path";
+import fs from "fs";
+import { Transaction, QueryInterface } from "sequelize";
+import { Sequelize } from "sequelize-typescript";
+import runMigrations from "../utils/runMigrations";
+import config from "../config/config";
+ 
+interface SequelizeTypeMap {
+  [key: string]: string[];
+}
+ 
+interface SequelizeType {
+  constructor: {
+    key: string;
+  };
+}
+ 
+interface ModelAttribute {
+  type: SequelizeType;
+  [key: string]: unknown;
+}
+ 
+const environment = process.env?.NODE_ENV || "production";
+const envConfig = config[environment as keyof typeof config];
+ 
+if (!envConfig) {
+  throw new Error(`No configuration found for environment: ${environment}`);
+}
+ 
+//external connection string
+const connectionString = process.env.DATABASE_URL;
+ 
+// Function to dynamically load models
+const loadModels = (): string[] => {
+  const modelsDir = __dirname;
+  try {
+    const modelFiles = fs.readdirSync(modelsDir);
+    return modelFiles
+      .filter(
+        (file) =>
+          (file.endsWith(".ts") || file.endsWith(".js")) &&
+          !file.startsWith("index"),
+      )
+      .map((file) => join(modelsDir, file));
+  } catch (error) {
+    console.error("Error loading models:", error);
+    throw new Error("Failed to load models.");
+  }
+};
+ 
+const models = loadModels();
+if (models.length === 0) {
+  throw new Error("No models found in the models directory.");
+}
+ 
+const sequelize = new Sequelize(`${connectionString}`, {
+  dialect: "postgres",
+  dialectOptions: {
+    ssl: {
+      require: true,
+      rejectUnauthorized: false, // Use this we use a service that uses a self-signed certificate
+    },
+  },
+  logging: console.log, // Set to false to disable SQL query logging
+  models: models,
+});
+ 
+/**
+ * Asynchronous (IIFE) for database synchronization and migration.
+ *
+ * This function performs the following tasks:
+ * 1. In development environment, it alters the database structure.
+ * 2. In production environment, it checks for schema, field, data-type changes and runs migrations if necessary.
+ * 3. Handles errors differently based on the environment.
+ *
+ * @async
+ * @function
+ * @throws {Error} Throws an error if database synchronization fails.
+ * @returns {Promise<void>} A promise that resolves when the database operations are complete.
+ */
+(async () => {
+  try {
+    if (environment === "development") {
+      console.log("Altering the database...");
+      await sequelize.sync({ alter: true });
+      console.log("✅ Database altered successfully.");
+    } else if (
+      environment === "production" &&
+      process.env.RUN_MIGRATIONS === "true"
+    ) {
+      // Use Sequelize's built-in schema comparison instead of raw SQL
+      console.log("Checking for schema changes...");
+ 
+      // Create a transaction to ensure consistency
+      const transaction: Transaction = await sequelize.transaction();
+ 
+      try {
+        // Use Sequelize's queryInterface for safer schema operations
+        const queryInterface: QueryInterface = sequelize.getQueryInterface();
+ 
+        // Track if we need to run migrations
+        let hasSchemaChanges = false;
+ 
+        // Check each model for changes
+        for (const modelName in sequelize.models) {
+          const model = sequelize.models[modelName] as unknown as {
+            tableName: string;
+            rawAttributes: Record<string, ModelAttribute>;
+          };
+          const tableName = model.tableName;
+ 
+          // Check if table exists
+          const tableExists = await queryInterface.tableExists(tableName, {
+            transaction,
+          });
+ 
+          if (!tableExists) {
+            console.log(`Schema change detected: New table ${tableName}`);
+            hasSchemaChanges = true;
+            break;
+          }
+ 
+          // Get current table description
+          const tableDescription = await queryInterface.describeTable(
+            tableName,
+            {},
+          );
+ 
+          // Get model attributes
+          const modelAttributes = model.rawAttributes;
+ 
+          // Check for new or modified columns
+          for (const attributeName in modelAttributes) {
+            const attribute = modelAttributes[attributeName];
+ 
+            // Skip virtual fields
+            if (attribute.type.constructor.key === "VIRTUAL") continue;
+ 
+            // Get the column info from database
+            const columnInfo = tableDescription[attributeName];
+ 
+            // If column doesn't exist, it's a new column
+            if (!columnInfo) {
+              console.log(
+                `Schema change detected: New column ${tableName}.${attributeName}`,
+              );
+              hasSchemaChanges = true;
+              break;
+            }
+ 
+            const sequelizeType = attribute.type.constructor.key;
+            const dbType = columnInfo.type;
+ 
+            // Check type compatibility
+            if (!isTypeCompatible(sequelizeType, dbType)) {
+              console.log(
+                `Schema change detected: Type change for ${tableName}.${attributeName}`,
+              );
+              hasSchemaChanges = true;
+              break;
+            }
+          }
+ 
+          if (hasSchemaChanges) break;
+ 
+          // Check for removed columns
+          for (const columnName in tableDescription) {
+            if (
+              !(columnName in modelAttributes) &&
+              !["id", "createdAt", "updatedAt"].includes(columnName)
+            ) {
+              // Skip standard Sequelize fields
+              console.log(
+                `Schema change detected: Removed column ${tableName}.${columnName}`,
+              );
+              hasSchemaChanges = true;
+              break;
+            }
+          }
+ 
+          if (hasSchemaChanges) break;
+        }
+ 
+        // Only run migrations if schema changes are detected
+        if (hasSchemaChanges) {
+          await transaction.commit();
+          console.log("Running migrations due to schema changes...");
+          await runMigrations(sequelize);
+          console.log("✅ Migrations completed successfully.");
+        } else {
+          await transaction.commit();
+          console.log("No schema changes detected. Skipping migrations.");
+        }
+      } catch (err) {
+        // Rollback transaction on error
+        await transaction.rollback();
+        throw err;
+      }
+    } else {
+      console.log("✅ Database is up to date.");
+    }
+  } catch (error) {
+    console.error("Error during database synchronization:", error);
+    // More graceful error handling for production
+    if (environment === "production") {
+      console.error(
+        "Database sync failed, but application will continue running with existing schema",
+      );
+      // Consider sending an alert to your monitoring system here
+    } else {
+      process.exit(1); // Only exit in development
+    }
+  }
+})();
+ 
+// Helper function to check type compatibility
+function isTypeCompatible(sequelizeType: string, dbType: string): boolean {
+  // This is a simplified version - expand based on your database and types
+  const typeMap: SequelizeTypeMap = {
+    STRING: ["character varying", "varchar", "text"],
+    INTEGER: ["integer", "int", "int4"],
+    BIGINT: ["bigint", "int8"],
+    FLOAT: ["real", "float4"],
+    DOUBLE: ["double precision", "float8"],
+    DECIMAL: ["numeric", "decimal"],
+    BOOLEAN: ["boolean", "bool"],
+    DATE: ["timestamp", "timestamptz", "date"],
+    DATEONLY: ["date"],
+    UUID: ["uuid"],
+    JSON: ["json", "jsonb"],
+    JSONB: ["jsonb"],
+    ARRAY: ["array"],
+  };
+ 
+  // Convert types to lowercase for comparison
+  const normalizedSequelizeType = sequelizeType.toUpperCase();
+  const normalizedDbType = dbType.toLowerCase();
+ 
+  // Check if the database type is compatible with the Sequelize type
+  return typeMap[normalizedSequelizeType]
+    ? typeMap[normalizedSequelizeType].includes(normalizedDbType)
+    : false;
+}
+ 
+export default sequelize;
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/farming-product-REST-api/src/models/notifiation.ts.html b/coverage/lcov-report/farming-product-REST-api/src/models/notifiation.ts.html new file mode 100644 index 0000000..45a9be6 --- /dev/null +++ b/coverage/lcov-report/farming-product-REST-api/src/models/notifiation.ts.html @@ -0,0 +1,268 @@ + + + + + + Code coverage report for farming-product-REST-api/src/models/notifiation.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api/src/models notifiation.ts

+
+ +
+ 100% + Statements + 41/41 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 100% + Functions + 0/0 +
+ + +
+ 100% + Lines + 41/41 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +1x +1x +1x +1x +1x +1x +  +  +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +  +1x +1x +  +1x +1x +1x +1x +1x +  +1x + 
"use strict";
+import {
+  Table,
+  Column,
+  Model,
+  DataType,
+  CreatedAt,
+  UpdatedAt,
+  ForeignKey,
+  BelongsTo,
+} from "sequelize-typescript";
+import {
+  NotificationAttributes,
+  NotificationCreationAttributes,
+} from "../types";
+import User from "./user";
+ 
+@Table({
+  timestamps: true,
+  tableName: "Notifications",
+  modelName: "Notification",
+})
+class Notification extends Model<
+  NotificationAttributes,
+  NotificationCreationAttributes
+> {
+  @Column({
+    primaryKey: true,
+    type: DataType.UUID,
+    defaultValue: DataType.UUIDV4,
+    allowNull: false,
+  })
+  declare id: string; // UUID
+  @Column({
+    type: DataType.STRING,
+  })
+  declare title?: string;
+  @Column({
+    type: DataType.STRING,
+  })
+  declare message?: string;
+  @Column({
+    type: DataType.BOOLEAN,
+  })
+  declare isRead: boolean;
+  @ForeignKey(() => User)
+  @Column({
+    type: DataType.UUID,
+  })
+  userId!: string; // UUID
+ 
+  @BelongsTo(() => User)
+  user!: User;
+ 
+  @CreatedAt
+  declare createdAt: Date;
+  @UpdatedAt
+  declare updatedAt: Date;
+}
+ 
+export default Notification;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/farming-product-REST-api/src/models/order.ts.html b/coverage/lcov-report/farming-product-REST-api/src/models/order.ts.html new file mode 100644 index 0000000..0446fac --- /dev/null +++ b/coverage/lcov-report/farming-product-REST-api/src/models/order.ts.html @@ -0,0 +1,382 @@ + + + + + + Code coverage report for farming-product-REST-api/src/models/order.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api/src/models order.ts

+
+ +
+ 100% + Statements + 77/77 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 100% + Functions + 0/0 +
+ + +
+ 100% + Lines + 77/77 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100  +1x +  +  +  +  +  +  +  +  +  +  +  +1x +1x +1x +1x +  +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +  +1x +1x +1x +1x +1x +  +1x +1x +1x +  +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +  +1x +1x +1x +1x +  +1x +1x +  +1x +1x +  +1x +1x +  +1x +1x +1x +1x +1x +  +1x + 
"use strict";
+import {
+  Table,
+  Column,
+  Model,
+  DataType,
+  CreatedAt,
+  UpdatedAt,
+  ForeignKey,
+  BelongsTo,
+  HasOne,
+} from "sequelize-typescript";
+import { OrderAttributes } from "../types";
+import User from "./user";
+import BuyerReview from "./buyerreview";
+import Product from "./product";
+import Transaction from "./transaction";
+ 
+@Table({
+  timestamps: true,
+  tableName: "Orders",
+  modelName: "Order",
+})
+class Order extends Model<OrderAttributes> {
+  @Column({
+    primaryKey: true,
+    type: DataType.UUID,
+    defaultValue: DataType.UUIDV4,
+    allowNull: false,
+  })
+  declare id: string; // UUID
+  @Column({
+    type: DataType.INTEGER,
+  })
+  declare amount?: number;
+  @Column({
+    type: DataType.STRING,
+  })
+  declare shipAddress?: string;
+  @Column({
+    type: DataType.STRING,
+  })
+  declare weight?: string;
+  @Column({
+    type: DataType.JSON,
+  })
+  declare review?: object | null;
+  @Column({
+    type: DataType.ENUM("pending", "processing", "delivered", "dispatched"),
+  })
+  declare status: string;
+ 
+  @ForeignKey(() => Product)
+  @Column({
+    type: DataType.UUID,
+  })
+  prodId!: string;
+ 
+  @ForeignKey(() => User)
+  @Column({ type: DataType.UUID })
+  buyerId!: string; // UUID
+ 
+  @ForeignKey(() => User)
+  @Column({ type: DataType.UUID })
+  sellerId!: string; // UUID
+  @Column({
+    type: DataType.JSON,
+  })
+  declare dispatchDetails?: object | null;
+  @Column({
+    type: DataType.BOOLEAN,
+  })
+  declare dispatched: boolean;
+  @Column({
+    type: DataType.DATE,
+  })
+  declare deliveryDate?: Date | null;
+ 
+  @HasOne(() => BuyerReview)
+  buyerReview?: BuyerReview; // UUID
+  @HasOne(() => Transaction)
+  transaction?: Transaction;
+ 
+  @BelongsTo(() => User, "buyerId")
+  buyer!: User;
+ 
+  @BelongsTo(() => User, "sellerId")
+  seller!: User;
+ 
+  @BelongsTo(() => Product, "prodId")
+  product!: Product;
+ 
+  @CreatedAt
+  declare createdAt: Date;
+  @UpdatedAt
+  declare updatedAt: Date;
+}
+ 
+export default Order;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/farming-product-REST-api/src/models/product.ts.html b/coverage/lcov-report/farming-product-REST-api/src/models/product.ts.html new file mode 100644 index 0000000..c3e3d4b --- /dev/null +++ b/coverage/lcov-report/farming-product-REST-api/src/models/product.ts.html @@ -0,0 +1,352 @@ + + + + + + Code coverage report for farming-product-REST-api/src/models/product.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api/src/models product.ts

+
+ +
+ 100% + Statements + 62/62 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 100% + Functions + 0/0 +
+ + +
+ 100% + Lines + 62/62 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90  +1x +  +  +  +  +  +  +  +  +  +  +  +  +1x +1x +1x +  +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +  +1x +1x +1x +1x +  +1x +1x +1x +1x +  +1x +1x +1x +1x +  +1x +1x +1x +1x +  +1x +1x +1x +1x +  +1x +1x +1x +1x +  +1x +1x +1x +1x +  +1x +1x +1x +1x +1x +  +1x +1x +  +1x +1x +  +1x +1x +  +1x +1x +1x +1x +1x +  +1x + 
"use strict";
+import {
+  Table,
+  Column,
+  Model,
+  DataType,
+  CreatedAt,
+  UpdatedAt,
+  ForeignKey,
+  BelongsTo,
+  HasMany,
+} from "sequelize-typescript";
+import { ProductAttributes } from "../types";
+ 
+import User from "./user";
+import BuyerReview from "./buyerreview";
+import Order from "./order";
+ 
+@Table({
+  timestamps: true,
+  tableName: "Products",
+  modelName: "Product",
+})
+class Product extends Model<ProductAttributes> {
+  @Column({
+    primaryKey: true,
+    type: DataType.UUID,
+    defaultValue: DataType.UUIDV4,
+    allowNull: false,
+  })
+  declare id: string; // UUID
+ 
+  @Column({
+    type: DataType.STRING,
+  })
+  declare productName?: string;
+ 
+  @Column({
+    type: DataType.STRING,
+  })
+  declare productCat?: string;
+ 
+  @Column({
+    type: DataType.STRING,
+  })
+  declare priceType?: string;
+ 
+  @Column({
+    type: DataType.FLOAT,
+  })
+  declare price?: number;
+ 
+  @Column({
+    type: DataType.STRING,
+  })
+  declare imageUrl?: string;
+ 
+  @Column({
+    type: DataType.STRING,
+  })
+  declare description?: string;
+ 
+  @Column({
+    type: DataType.BOOLEAN,
+  })
+  declare wholeSale?: boolean;
+ 
+  @ForeignKey(() => User)
+  @Column({
+    type: DataType.UUID,
+  })
+  userId!: string; // UUID
+ 
+  @BelongsTo(() => User)
+  user!: User;
+ 
+  @HasMany(() => BuyerReview)
+  buyerReview?: BuyerReview[];
+ 
+  @HasMany(() => Order)
+  order?: Order[];
+ 
+  @CreatedAt
+  declare createdAt: Date;
+  @UpdatedAt
+  declare updatedAt: Date;
+}
+ 
+export default Product;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/farming-product-REST-api/src/models/role.ts.html b/coverage/lcov-report/farming-product-REST-api/src/models/role.ts.html new file mode 100644 index 0000000..b18b390 --- /dev/null +++ b/coverage/lcov-report/farming-product-REST-api/src/models/role.ts.html @@ -0,0 +1,169 @@ + + + + + + Code coverage report for farming-product-REST-api/src/models/role.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api/src/models role.ts

+
+ +
+ 100% + Statements + 23/23 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 100% + Functions + 0/0 +
+ + +
+ 100% + Lines + 23/23 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +291x +  +1x +  +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +  +1x +1x +1x +1x +  +1x +1x +1x +  +1x + 
import { Table, Column, Model, DataType, HasMany } from "sequelize-typescript";
+import { RoleAttributes } from "../types";
+import User from "./user";
+ 
+@Table({
+  timestamps: true,
+  tableName: "Roles",
+  modelName: "Role",
+})
+class Role extends Model<RoleAttributes> {
+  @Column({
+    primaryKey: true,
+    type: DataType.UUID,
+    defaultValue: DataType.UUIDV4,
+    allowNull: false,
+  })
+  declare id: string;
+ 
+  @Column({
+    type: DataType.STRING,
+  })
+  declare roleName: "farmer" | "buyer";
+ 
+  @HasMany(() => User, "roleId")
+  user?: User[];
+}
+ 
+export default Role;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/farming-product-REST-api/src/models/transaction.ts.html b/coverage/lcov-report/farming-product-REST-api/src/models/transaction.ts.html new file mode 100644 index 0000000..6518879 --- /dev/null +++ b/coverage/lcov-report/farming-product-REST-api/src/models/transaction.ts.html @@ -0,0 +1,280 @@ + + + + + + Code coverage report for farming-product-REST-api/src/models/transaction.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api/src/models transaction.ts

+
+ +
+ 100% + Statements + 52/52 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 100% + Functions + 0/0 +
+ + +
+ 100% + Lines + 52/52 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +661x +  +  +  +  +  +  +  +  +  +1x +  +  +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +  +1x +1x +1x +1x +1x +1x +1x +  +1x + 
import {
+  Table,
+  Column,
+  Model,
+  DataType,
+  CreatedAt,
+  UpdatedAt,
+  ForeignKey,
+  BelongsTo,
+} from "sequelize-typescript";
+import Order from "./order";
+import { TransactionAttributes } from "../types";
+ 
+@Table({
+  timestamps: true,
+  tableName: "Transactions",
+  modelName: "Transaction",
+})
+class Transaction extends Model<TransactionAttributes> {
+  @Column({
+    primaryKey: true,
+    type: DataType.UUID,
+    defaultValue: DataType.UUIDV4,
+    allowNull: false,
+  })
+  declare id: string; // UUID
+  @Column({
+    type: DataType.INTEGER,
+  })
+  declare amount?: number;
+  @Column({
+    type: DataType.ENUM("pending", "completed", "rejected"),
+  })
+  declare status: string;
+  @Column({
+    type: DataType.ENUM("Payment", "Refund"),
+  })
+  declare txType: string;
+  @Column({
+    type: DataType.ENUM("MOBILE-MONEY", "ORANGE-MONEY", "VISA", "MASTERCARD"),
+  })
+  declare txMethod?: string;
+  @Column({
+    type: DataType.JSON,
+  })
+  declare txDetails?: object | null;
+  @Column({
+    type: DataType.STRING,
+  })
+  declare currency: string;
+  @ForeignKey(() => Order)
+  @Column({
+    type: DataType.UUID,
+  })
+  orderId!: string; // UUID
+ 
+  @BelongsTo(() => Order)
+  order!: Order;
+  @CreatedAt
+  declare createdAt: Date;
+  @UpdatedAt
+  declare updatedAt: Date;
+}
+ 
+export default Transaction;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/farming-product-REST-api/src/models/user.ts.html b/coverage/lcov-report/farming-product-REST-api/src/models/user.ts.html new file mode 100644 index 0000000..b943b40 --- /dev/null +++ b/coverage/lcov-report/farming-product-REST-api/src/models/user.ts.html @@ -0,0 +1,433 @@ + + + + + + Code coverage report for farming-product-REST-api/src/models/user.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api/src/models user.ts

+
+ +
+ 100% + Statements + 96/96 +
+ + +
+ 100% + Branches + 5/5 +
+ + +
+ 100% + Functions + 0/0 +
+ + +
+ 100% + Lines + 96/96 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +1171x +  +  +  +  +  +  +  +  +  +  +  +1x +1x +1x +1x +  +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +  +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +  +1x +1x +  +1x +1x +  +1x +1x +  +1x +1x +  +1x +1x +  +1x +1x +1x +1x +1x +  +1x + 
import {
+  Table,
+  Column,
+  Model,
+  DataType,
+  CreatedAt,
+  UpdatedAt,
+  ForeignKey,
+  HasMany,
+  BelongsTo,
+} from "sequelize-typescript";
+import { UserAttributes } from "../types";
+import Role from "./role";
+import Order from "./order";
+import BuyerReview from "./buyerreview";
+import Product from "./product";
+ 
+@Table({
+  timestamps: true,
+  tableName: "Users",
+  modelName: "User",
+})
+class User extends Model<UserAttributes> {
+  @Column({
+    primaryKey: true,
+    type: DataType.UUID,
+    defaultValue: DataType.UUIDV4,
+    allowNull: false,
+  })
+  declare id: string;
+  @Column({
+    type: DataType.STRING,
+  })
+  declare firstName: string;
+  @Column({
+    type: DataType.STRING,
+  })
+  declare lastName: string;
+  @Column({
+    type: DataType.STRING,
+  })
+  declare email: string;
+  @Column({
+    type: DataType.STRING,
+  })
+  declare address: string;
+  @Column({
+    type: DataType.JSON,
+    defaultValue: [],
+  })
+  declare shipAddress: object[];
+  @Column({
+    type: DataType.STRING,
+  })
+  declare country: string;
+  @Column({
+    type: DataType.STRING,
+  })
+  declare imageUrl: string;
+  @Column({
+    type: DataType.STRING,
+  })
+  declare googleId: string;
+  @Column({
+    type: DataType.STRING,
+  })
+  declare facebookId: string;
+  @Column({
+    type: DataType.BIGINT,
+  })
+  declare phoneNum: number;
+  @Column({
+    type: DataType.STRING,
+  })
+  declare password: string | null;
+  @Column({
+    type: DataType.STRING,
+  })
+  declare expoPushToken: string | null;
+ 
+  @Column({
+    type: DataType.BOOLEAN,
+  })
+  declare vip: boolean;
+  @Column({
+    type: DataType.BOOLEAN,
+  })
+  declare verifiedUser: boolean;
+  @ForeignKey(() => Role)
+  @Column({
+    type: DataType.UUID,
+  })
+  roleId!: string;
+ 
+  @BelongsTo(() => Role)
+  role!: Role;
+ 
+  @HasMany(() => Order, "buyerId")
+  ordersAsBuyer?: Order[];
+ 
+  @HasMany(() => Order, "sellerId")
+  ordersAsSeller?: Order[];
+ 
+  @HasMany(() => Product, "userId")
+  product?: Product[];
+ 
+  @HasMany(() => BuyerReview, "userId")
+  buyerReviews?: BuyerReview[];
+ 
+  @CreatedAt
+  declare createdAt: Date;
+  @UpdatedAt
+  declare updatedAt: Date;
+}
+ 
+export default User;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/farming-product-REST-api/src/models/userotpcode.ts.html b/coverage/lcov-report/farming-product-REST-api/src/models/userotpcode.ts.html new file mode 100644 index 0000000..1208009 --- /dev/null +++ b/coverage/lcov-report/farming-product-REST-api/src/models/userotpcode.ts.html @@ -0,0 +1,226 @@ + + + + + + Code coverage report for farming-product-REST-api/src/models/userotpcode.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api/src/models userotpcode.ts

+
+ +
+ 100% + Statements + 37/37 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 100% + Functions + 0/0 +
+ + +
+ 100% + Lines + 37/37 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +481x +  +  +  +  +  +  +  +  +  +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +  +1x + 
import {
+  Table,
+  Column,
+  Model,
+  DataType,
+  CreatedAt,
+  UpdatedAt,
+  ForeignKey,
+} from "sequelize-typescript";
+import { UserOTPAttributes } from "../types";
+import User from "./user";
+@Table({
+  timestamps: true,
+  tableName: "userOTPCodes",
+  modelName: "UserOTPCode",
+})
+class UserOTPCode extends Model<UserOTPAttributes> {
+  @Column({
+    primaryKey: true,
+    type: DataType.UUID,
+    defaultValue: DataType.UUIDV4,
+    allowNull: false,
+  })
+  declare id: string;
+  @Column({
+    type: DataType.STRING,
+  })
+  @ForeignKey(() => User)
+  @Column({
+    type: DataType.UUID,
+  })
+  userId!: string;
+  @Column({
+    type: DataType.STRING,
+  })
+  declare otp: string;
+  @Column({
+    type: DataType.BIGINT,
+  })
+  declare expiredAt: number;
+  @CreatedAt
+  declare createdAt: Date;
+  @UpdatedAt
+  declare updatedAt: Date;
+}
+ 
+export default UserOTPCode;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/farming-product-REST-api/src/routes/auth.routes.ts.html b/coverage/lcov-report/farming-product-REST-api/src/routes/auth.routes.ts.html new file mode 100644 index 0000000..1d9d717 --- /dev/null +++ b/coverage/lcov-report/farming-product-REST-api/src/routes/auth.routes.ts.html @@ -0,0 +1,1081 @@ + + + + + + Code coverage report for farming-product-REST-api/src/routes/auth.routes.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api/src/routes auth.routes.ts

+
+ +
+ 100% + Statements + 11/11 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 100% + Functions + 0/0 +
+ + +
+ 100% + Lines + 11/11 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +3331x +1x +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +1x + 
import express, { Router } from "express";
+import {
+  verifyPhone,
+  register_user,
+  logIn,
+  verifyOtp,
+  authHandler,
+  sendNewOTP,
+  refreshToken,
+} from "../controllers/auth.controller";
+ 
+const authRouter: Router = express.Router();
+ 
+/**
+ * @swagger
+ * tags:
+ *   name: Authentication
+ *   description: Endpoints for user authentication, registration, and JWT management
+ */
+// signup
+/**
+ * @swagger
+ * /auth/signup:
+ *  post:
+ *    summary: phone verification api
+ *    tags: [Authentication]
+ *    requestBody:
+ *      required: true
+ *      content:
+ *        application/json:
+ *          schema:
+ *            type: object
+ *            properties:
+ *              email:
+ *                type: string
+ *              password:
+ *                type: string
+ *              userRole:
+ *                type: string
+ *              phoneNum:
+ *                type: string
+ *              country:
+ *                type: string
+ *
+ *
+ *    responses:
+ *      200:
+ *        description: The OTP sent successfully
+ *
+ *      500:
+ *        description: Some server error
+ *
+ */
+authRouter.post("/signup", verifyPhone);
+ 
+// verify OTP
+ 
+/**
+ * @swagger
+ * /auth/verifyOTP:
+ *  post:
+ *    summary: phone verification api
+ *    tags: [Authentication]
+ *    requestBody:
+ *      required: true
+ *      content:
+ *        application/json:
+ *          schema:
+ *            type: object
+ *            properties:
+ *              email:
+ *                type: string
+ *              otp:
+ *                type: string
+ *    responses:
+ *      200:
+ *        description: The OTP sent successfully
+ *
+ *      500:
+ *        description: Some server error
+ *
+ */
+authRouter.post("/verifyOTP", verifyOtp);
+ 
+// user registration
+ 
+/**
+ * @swagger
+ * /auth/signup/{userId}:
+ *  put:
+ *    summary: phone verification api
+ *    tags: [Authentication]
+ *    parameters:
+ *      - in: path
+ *        name: userId
+ *        schema:
+ *          type: string
+ *        required: true
+ *    requestBody:
+ *      required: true
+ *      content:
+ *        multipart/form-data:
+ *          schema:
+ *            type: object
+ *            properties:
+ *              firstName:
+ *                type: string
+ *              lastName:
+ *                type: string
+ *              address:
+ *                type: string
+ *              profileImage:
+ *                type: string
+ *                format: binary
+ *    responses:
+ *      200:
+ *        description: The book sent successfully
+ *
+ *      500:
+ *        description: Some server error
+ *
+ */
+authRouter.put("/signup/:userId", register_user);
+ 
+// login
+ 
+/**
+ * @swagger
+ * /auth/login:
+ *   post:
+ *     summary: Log in with email and password
+ *     tags: [Authentication]
+ *     requestBody:
+ *       required: true
+ *       content:
+ *         application/json:
+ *           schema:
+ *             type: object
+ *             properties:
+ *               email:
+ *                 type: string
+ *               password:
+ *                 type: string
+ *     responses:
+ *       200:
+ *         description: OK
+ *         content:
+ *           application/json:
+ *             schema:
+ *               type: object
+ *               properties:
+ *                 message:
+ *                   type: string
+ *                   example: "Authentication Successful"
+ *                 token:
+ *                   type: string
+ *                   description: JWT access token
+ *                 refreshToken:
+ *                   type: string
+ *                   description: JWT refresh token
+ *                 userData:
+ *                   type: object
+ *                   description: User information
+ *       500:
+ *         description: Internal Server Error
+ *         content:
+ *           application/json:
+ *             schema:
+ *               type: object
+ *               properties:
+ *                 error:
+ *                   type: string
+ *                 message:
+ *                   type: string
+ */
+authRouter.post("/login", logIn);
+ 
+// send OTP
+ 
+/**
+ * @swagger
+ * /auth/sendOTP:
+ *  post:
+ *    summary: phone verification api
+ *    tags: [Authentication]
+ *    requestBody:
+ *      required: true
+ *      content:
+ *        application/json:
+ *          schema:
+ *            type: object
+ *            properties:
+ *              email:
+ *                type: string
+ *    responses:
+ *      200:
+ *        description: The OTP sent successfully
+ *
+ *      500:
+ *        description: Some server error
+ *
+ */
+authRouter.post("/sendOTP/", sendNewOTP);
+ 
+// oAuth
+ 
+/**
+ * @swagger
+ * /auth/signup/oAuth:
+ *   post:
+ *     summary: Authenticate using Google or Facebook OAuth
+ *     tags: [Authentication]
+ *     description: Authenticates a user using either a Google token or Facebook token
+ *     requestBody:
+ *       required: true
+ *       content:
+ *         application/json:
+ *           schema:
+ *             type: object
+ *             properties:
+ *               googleToken:
+ *                 type: string
+ *                 description: Google OAuth ID token
+ *               facebookToken:
+ *                 type: string
+ *                 description: Facebook access token
+ *             example:
+ *               googleToken: "eyJhbGciOiJSUzI1NiIsImtpZCI6IjFlOTczZWUyZT..."
+ *     responses:
+ *       200:
+ *         description: User authenticated successfully
+ *         content:
+ *           application/json:
+ *             schema:
+ *               type: object
+ *               properties:
+ *                 message:
+ *                   type: string
+ *                   example: "User logged in successfully"
+ *                 token:
+ *                   type: string
+ *                   description: JWT access token
+ *                   example: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
+ *                 user:
+ *                   type: object
+ *                   properties:
+ *                     id:
+ *                       type: string
+ *                       example: "60d21b4667d0d8992e610c85"
+ *                     email:
+ *                       type: string
+ *                       example: "user@example.com"
+ *                     firstName:
+ *                       type: string
+ *                       example: "John"
+ *                     lastName:
+ *                       type: string
+ *                       example: "Doe"
+ *                     imageUrl:
+ *                       type: string
+ *                       example: "https://lh3.googleusercontent.com/a/user_photo"
+ *                     roleId:
+ *                       type: string
+ *                       example: "60d21b4667d0d8992e610c86"
+ *       400:
+ *         description: Bad request
+ *         content:
+ *           application/json:
+ *             schema:
+ *               type: object
+ *               properties:
+ *                 message:
+ *                   type: string
+ *                   example: "Missing authentication token"
+ *       401:
+ *         description: Unauthorized
+ *         content:
+ *           application/json:
+ *             schema:
+ *               type: object
+ *               properties:
+ *                 message:
+ *                   type: string
+ *                   example: "Invalid Google token"
+ *       500:
+ *         description: Server error
+ *         content:
+ *           application/json:
+ *             schema:
+ *               type: object
+ *               properties:
+ *                 message:
+ *                   type: string
+ *                   example: "User creation failed. Please try again."
+ */
+authRouter.post("/signup/oAuth", authHandler);
+ 
+// refresh token to generate access token
+/**
+ * @swagger
+ * /auth/refreshToken:
+ *   post:
+ *     summary: Refresh access token
+ *     tags: [Authentication]
+ *     security:
+ *       - bearerAuth: []
+ *     description: Generates a new access token using the refresh token provided in the Authorization header
+ *     responses:
+ *       200:
+ *         description: New access token generated successfully
+ *         content:
+ *           application/json:
+ *             schema:
+ *               type: object
+ *               properties:
+ *                 accessToken:
+ *                   type: string
+ *                   description: The new access token
+ *                 message:
+ *                   type: string
+ *                   description: the success message
+ *               example:
+ *                 accessToken: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
+ *                 message: Token refreshed successfully...
+ *       401:
+ *         description: Expired or invalid refresh token
+ *       403:
+ *         description: Forbidden - token has been invalidated
+ */
+authRouter.post("/refreshToken", refreshToken);
+ 
+export default authRouter;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/farming-product-REST-api/src/routes/index.html b/coverage/lcov-report/farming-product-REST-api/src/routes/index.html new file mode 100644 index 0000000..6057bcd --- /dev/null +++ b/coverage/lcov-report/farming-product-REST-api/src/routes/index.html @@ -0,0 +1,221 @@ + + + + + + Code coverage report for farming-product-REST-api/src/routes + + + + + + + + + +
+
+

All files farming-product-REST-api/src/routes

+
+ +
+ 72.39% + Statements + 160/221 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/6 +
+ + +
+ 72.39% + Lines + 160/221 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
auth.routes.ts +
+
100%11/11100%0/0100%0/0100%11/11
index.ts +
+
100%18/18100%0/0100%0/0100%18/18
notification.routes.ts +
+
73.07%19/26100%0/00%0/173.07%19/26
order.routes.ts +
+
71.42%25/35100%0/00%0/171.42%25/35
payment.collection.routes.ts +
+
64.51%20/31100%0/00%0/164.51%20/31
product.routes.ts +
+
73.33%33/45100%0/00%0/173.33%33/45
review.routes.ts +
+
60%15/25100%0/00%0/160%15/25
user.routes.ts +
+
63.33%19/30100%0/00%0/163.33%19/30
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/farming-product-REST-api/src/routes/index.ts.html b/coverage/lcov-report/farming-product-REST-api/src/routes/index.ts.html new file mode 100644 index 0000000..1952965 --- /dev/null +++ b/coverage/lcov-report/farming-product-REST-api/src/routes/index.ts.html @@ -0,0 +1,148 @@ + + + + + + Code coverage report for farming-product-REST-api/src/routes/index.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api/src/routes index.ts

+
+ +
+ 100% + Statements + 18/18 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 100% + Functions + 0/0 +
+ + +
+ 100% + Lines + 18/18 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +221x +1x +1x +1x +1x +1x +1x +1x +  +1x +  +1x +1x +1x +1x +1x +1x +1x +1x +  +1x + 
import express, { Router } from "express";
+import authRoutes from "./auth.routes";
+import userRoutes from "./user.routes";
+import productRouter from "./product.routes";
+import reviewRouter from "./review.routes";
+import orderRouter from "./order.routes";
+import paymentCollectionRoute from "./payment.collection.routes";
+import notificationRouter from "./notification.routes";
+ 
+const appRouter: Router = express.Router();
+ 
+console.log("\n\n from route index file: \n\n");
+appRouter.use("/auth", authRoutes);
+appRouter.use("/users", userRoutes);
+appRouter.use("/products", productRouter);
+appRouter.use("/reviews", reviewRouter);
+appRouter.use("/orders", orderRouter);
+appRouter.use("/transactions", paymentCollectionRoute);
+appRouter.use("/notifications", notificationRouter);
+ 
+export default appRouter;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/farming-product-REST-api/src/routes/notification.routes.ts.html b/coverage/lcov-report/farming-product-REST-api/src/routes/notification.routes.ts.html new file mode 100644 index 0000000..2ab6db8 --- /dev/null +++ b/coverage/lcov-report/farming-product-REST-api/src/routes/notification.routes.ts.html @@ -0,0 +1,862 @@ + + + + + + Code coverage report for farming-product-REST-api/src/routes/notification.routes.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api/src/routes notification.routes.ts

+
+ +
+ 73.07% + Statements + 19/26 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 73.07% + Lines + 19/26 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +2601x +1x +1x +  +1x +  +  +  +  +  +  +1x +1x +1x +1x +  +  +  +  +  +  +  +  +  +1x +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +1x +1x +1x +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +1x + 
import express, { NextFunction, Request, Response, Router } from "express";
+import authCheck from "../middleware/auth-check";
+import { z } from "zod";
+ 
+import {
+  createNotification,
+  getNotification,
+  markAsRead,
+  testExpoNotification,
+} from "../controllers/notification.controller";
+ 
+const notificationSchema = z.object({
+  title: z.string().min(1),
+  message: z.string().min(1),
+});
+ 
+function validateNotification(req: Request, res: Response, next: NextFunction) {
+  const parsed = notificationSchema.safeParse(req.body);
+  if (!parsed.success) {
+    return res.status(400).json({ errors: parsed.error.flatten().fieldErrors });
+  }
+  next();
+}
+ 
+const notificationRouter: Router = express.Router();
+ 
+notificationRouter.use(authCheck);
+/**
+ * @swagger
+ * components:
+ *   schemas:
+ *     Notification:
+ *       type: object
+ *       required:
+ *         - userId
+ *         - title
+ *         - message
+ *       properties:
+ *         id:
+ *           type: string
+ *           description: The auto-generated id of the notification
+ *         userId:
+ *           type: string
+ *           description: The user ID this notification belongs to
+ *         title:
+ *           type: string
+ *           description: The title of the notification
+ *         message:
+ *           type: string
+ *           description: The content of the notification
+ *         isRead:
+ *           type: boolean
+ *           description: Indicates if the notification has been read
+ *         createdAt:
+ *           type: string
+ *           format: date-time
+ *           description: The date the notification was created
+ *       example:
+ *         id: 60d21b4667d0d8992e610c85
+ *         userId: 60d21b4667d0d8992e610c86
+ *         title: New Order
+ *         message: Your order has been received
+ *         isRead: false
+ *         createdAt: 2023-01-10T04:05:06.157Z
+ */
+ 
+/**
+ * @swagger
+ * tags:
+ *   name: Notifications
+ *   description: API for managing user notifications
+ */
+ 
+/**
+ * @swagger
+ * /notifications/create/{userId}:
+ *   post:
+ *     summary: Create a new notification for a user
+ *     tags: [Notifications]
+ *     parameters:
+ *       - in: path
+ *         name: userId
+ *         schema:
+ *           type: string
+ *         required: true
+ *         description: The user id
+ *     requestBody:
+ *       required: true
+ *       content:
+ *         application/json:
+ *           schema:
+ *             type: object
+ *             required:
+ *               - title
+ *               - message
+ *             properties:
+ *               title:
+ *                 type: string
+ *                 description: Notification title
+ *               message:
+ *                 type: string
+ *                 description: Notification message
+ *             example:
+ *               title: "Order Update"
+ *               message: "Your order has been shipped"
+ *     responses:
+ *       200:
+ *         description: Notification created successfully
+ *         content:
+ *           application/json:
+ *             schema:
+ *               type: object
+ *               properties:
+ *                 result:
+ *                   $ref: '#/components/schemas/Notification'
+ *       500:
+ *         description: Server error
+ */
+notificationRouter.post(
+  "/create/:userId",
+  validateNotification,
+  createNotification,
+);
+ 
+/**
+ * @swagger
+ * /notifications/{userId}:
+ *   get:
+ *     summary: Get all notifications for a user
+ *     tags: [Notifications]
+ *     parameters:
+ *       - in: path
+ *         name: userId
+ *         schema:
+ *           type: string
+ *         required: true
+ *         description: The user id
+ *     responses:
+ *       200:
+ *         description: List of notifications for the user
+ *         content:
+ *           application/json:
+ *             schema:
+ *               type: object
+ *               properties:
+ *                 notifiations:
+ *                   type: object
+ *                   properties:
+ *                     count:
+ *                       type: integer
+ *                       example: 2
+ *                     rows:
+ *                       type: array
+ *                       items:
+ *                         type: object
+ *                         properties:
+ *                           id:
+ *                             type: string
+ *                             example: "60d21b4667d0d8992e610c85"
+ *                           userId:
+ *                             type: string
+ *                             example: "60d21b4667d0d8992e610c86"
+ *                           title:
+ *                             type: string
+ *                             example: "New Order"
+ *                           message:
+ *                             type: string
+ *                             example: "Your order has been received"
+ *                           isRead:
+ *                             type: boolean
+ *                             example: false
+ *                           createdAt:
+ *                             type: string
+ *                             format: date-time
+ *                             example: "2023-01-10T04:05:06.157Z"
+ *                           User:
+ *                             type: object
+ *                             properties:
+ *                               id:
+ *                                 type: string
+ *                                 example: "60d21b4667d0d8992e610c86"
+ *                               firstName:
+ *                                 type: string
+ *                                 example: "John"
+ *                               lastName:
+ *                                 type: string
+ *                                 example: "Doe"
+ *                               country:
+ *                                 type: string
+ *                                 example: "Cameroon"
+ *                               verifiedUser:
+ *                                 type: boolean
+ *                                 example: true
+ *       500:
+ *         description: Server error
+ */
+notificationRouter.get("/:userId", getNotification);
+ 
+/**
+ * @swagger
+ * /notifications/{id}:
+ *   put:
+ *     summary: Mark a notification as read
+ *     tags: [Notifications]
+ *     parameters:
+ *       - in: path
+ *         name: id
+ *         schema:
+ *           type: string
+ *         required: true
+ *         description: The notification id
+ *     responses:
+ *       200:
+ *         description: Notification marked as read
+ *         content:
+ *           application/json:
+ *             schema:
+ *               type: object
+ *               properties:
+ *                 message:
+ *                   type: string
+ *                   example: "success"
+ *       500:
+ *         description: Server error
+ */
+notificationRouter.put("/:id", markAsRead);
+ 
+// test expo notification
+ 
+/**
+ * @swagger
+ * /notifications/{userId}/test:
+ *   get:
+ *     summary: Send a test push notification to user's device
+ *     tags: [Notifications]
+ *     parameters:
+ *       - in: path
+ *         name: userId
+ *         schema:
+ *           type: string
+ *         required: true
+ *         description: The user id
+ *     responses:
+ *       200:
+ *         description: Test notification sent
+ *         content:
+ *           application/json:
+ *             schema:
+ *               type: object
+ *               properties:
+ *                 message:
+ *                   type: object
+ *                   description: Response from Expo push notification service
+ *       500:
+ *         description: Server error
+ */
+notificationRouter.get("/:userId/test", testExpoNotification);
+ 
+export default notificationRouter;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/farming-product-REST-api/src/routes/order.routes.ts.html b/coverage/lcov-report/farming-product-REST-api/src/routes/order.routes.ts.html new file mode 100644 index 0000000..e93e26f --- /dev/null +++ b/coverage/lcov-report/farming-product-REST-api/src/routes/order.routes.ts.html @@ -0,0 +1,883 @@ + + + + + + Code coverage report for farming-product-REST-api/src/routes/order.routes.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api/src/routes order.routes.ts

+
+ +
+ 71.42% + Statements + 25/35 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 71.42% + Lines + 25/35 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +2671x +1x +1x +  +  +  +  +  +  +  +  +1x +1x +  +1x +1x +1x +1x +1x +1x +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +1x +1x +1x +1x +1x +  +1x +  +1x + 
import express, { NextFunction, Request, Response, Router } from "express";
+import authCheck from "../middleware/auth-check";
+import {
+  getOrderById,
+  createOrder,
+  updateOrder,
+  getBuyerOrders,
+  getSellerOrders,
+  getTransaction,
+  updateDispatchDetails,
+} from "../controllers/order.controller";
+import uploadMiddleware from "../middleware/multerStorage";
+import { z } from "zod";
+ 
+const orderSchema = z.object({
+  shipAddress: z.string().min(1),
+  weight: z.string().min(1),
+  sellerId: z.string().uuid(),
+  amount: z.number().min(1),
+});
+ 
+function validateOrder(req: Request, res: Response, next: NextFunction) {
+  const parsed = orderSchema.safeParse({
+    ...req.body,
+    amount: req.body.amount ? Number(req.body.amount) : undefined,
+  });
+  if (!parsed.success) {
+    return res.status(400).json({ errors: parsed.error.flatten().fieldErrors });
+  }
+  next();
+}
+ 
+const orderRouter: Router = express.Router();
+ 
+orderRouter.use(authCheck);
+/**
+ * @swagger
+ * components:
+ *   schemas:
+ *     order:
+ *       type: object
+ *       required:
+ *         - userId
+ *         - title
+ *         - message
+ *       properties:
+ *         id:
+ *           type: string
+ *           description: The auto-generated id of the order
+ *         buyerId:
+ *           type: string
+ *           description: The user ID for the buyer of this order belongs to
+ *         sellerId:
+ *           type: string
+ *           description: The user ID for the seller of this order belongs to
+ *         amoount:
+ *           type: string
+ *           description: The amount of the order
+ *         weight:
+ *           type: string
+ *           description: The weight of the order
+ *         review:
+ *           type: object
+ *           description: Indicates the review made for the order.
+ *         status:
+ *           type: string
+ *           description: The status of the order
+ *         dispatchDetails:
+ *           type: any
+ *           description: The dispatch details of the order
+ *         dispatched:
+ *           type: boolean
+ *           description: Indicates if the order has been dispatched
+ *         deliveryDate:
+ *           type: Date
+ *           description: The delivery date of the order
+ *         prodId:
+ *           type: string
+ *           description: The product ID of the order
+ *         createdAt:
+ *           type: string
+ *           format: date-time
+ *           description: The date the order was created
+ *         updatedAt:
+ *           type: string
+ *           format: date-time
+ *           description: The date the order was updated
+ *       example:
+ *         id: 60d21b4667d0d8992e610c85
+ *         userId: 60d21b4667d0d8992e610c86
+ *         title: New Order
+ *         message: Your order has been received
+ *         isRead: false
+ *         createdAt: 2023-01-10T04:05:06.157Z
+ */
+ 
+/**
+ * @swagger
+ * tags:
+ *   name: Orders
+ *   description: API for managing user orders
+ */
+ 
+/**
+ * @swagger
+ * /user/order/buyer/{buyerId}:
+ *  get:
+ *      summary: all order of a buyer
+ *      tags: [Orders]
+ *      security:
+ *          - bearerAuth: []
+ *      parameters:
+ *        - in: path
+ *          name: buyerId
+ *          schema:
+ *              type: string
+ *          required: true
+ *        - in: query
+ *          name: orderStatus
+ *          schema:
+ *              type: string
+ *          required: false
+ *      responses:
+ *          200:
+ *              description: Ok. a product Data based on the product ID
+ *          401:
+ *              description: Forbiden or unauthorized
+ *          500:
+ *              description: Server Error
+ */
+// get all orders of the buyer with query parameter of status
+orderRouter.get("/buyer/:buyerId", getBuyerOrders);
+ 
+/**
+ * @swagger
+ * /user/order/seller/{sellerId}:
+ *  get:
+ *      summary: all order of a seller
+ *      tags: [Orders]
+ *      security:
+ *          - bearerAuth: []
+ *      parameters:
+ *        - in: path
+ *          name: sellerId
+ *          schema:
+ *              type: string
+ *          required: true
+ *        - in: query
+ *          name: orderStatus
+ *          schema:
+ *              type: string
+ *          required: false
+ *      responses:
+ *          200:
+ *              description: Ok. a product Data based on the product ID
+ *          401:
+ *              description: Forbiden or unauthorized
+ *          500:
+ *              description: Server Error
+ */
+ 
+// get all orders of the seller with query parameter of status
+orderRouter.get("/seller/:sellerId", getSellerOrders);
+ 
+/**
+ * @swagger
+ * /user/order/{orderId}:
+ *  get:
+ *      summary: fetching an order using ID
+ *      tags: [Orders]
+ *      security:
+ *          - bearerAuth: []
+ *      parameters:
+ *        - in: path
+ *          name: orderId
+ *          schema:
+ *              type: string
+ *          required: true
+ *      responses:
+ *          200:
+ *              description: Ok. a product Data based on the product ID
+ *          401:
+ *              description: Forbiden or unauthorized
+ *          500:
+ *              description: Server Error
+ */
+ 
+orderRouter.get("/:orderId", getOrderById);
+ 
+/**
+ * @swagger
+ * /user/order/new/{productId}:
+ *  post:
+ *      summary: Place an order to the product
+ *      tags: [Orders]
+ *      security:
+ *          - bearerAuth: []
+ *      parameters:
+ *        - in: path
+ *          name: productId
+ *          schema:
+ *              type: string
+ *          required: true
+ *      requestBody:
+ *          required: true
+ *          content:
+ *              application/json:
+ *                  schema:
+ *                      type: object
+ *                      properties:
+ *                          shipAddress:
+ *                              type: string
+ *                          weight:
+ *                              type: string
+ *                          sellerId:
+ *                              type: string
+ *                          amount:
+ *                              type: integer
+ *
+ *      responses:
+ *          200:
+ *              description: OK
+ *          403:
+ *              description: Forbiden
+ *          500:
+ *              description: Internal Server Error
+ *          429:
+ *              description: Too many requests
+ */
+orderRouter.post("/new/:productId", validateOrder, createOrder);
+ 
+/**
+ * @swagger
+ * /user/order/{orderId}/complete:
+ *  put:
+ *      summary: Make the order status as complete
+ *      tags: [Orders]
+ *      security:
+ *          - bearerAuth: []
+ *      parameters:
+ *        - in: path
+ *          name: orderId
+ *          schema:
+ *              type: string
+ *          required: true
+ *      responses:
+ *          200:
+ *              description: OK
+ *          403:
+ *              description: Forbiden
+ *          500:
+ *              description: Internal Server Error
+ *          429:
+ *              description: Too many requests
+ */
+orderRouter.put("/:orderId/complete", updateOrder);
+ 
+orderRouter.put(
+  "/:orderId/dispatch",
+  uploadMiddleware("dispatchImage"),
+  updateDispatchDetails,
+);
+ 
+orderRouter.get("/:orderId/transaction", getTransaction);
+ 
+export default orderRouter;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/farming-product-REST-api/src/routes/payment.collection.routes.ts.html b/coverage/lcov-report/farming-product-REST-api/src/routes/payment.collection.routes.ts.html new file mode 100644 index 0000000..41142a5 --- /dev/null +++ b/coverage/lcov-report/farming-product-REST-api/src/routes/payment.collection.routes.ts.html @@ -0,0 +1,610 @@ + + + + + + Code coverage report for farming-product-REST-api/src/routes/payment.collection.routes.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api/src/routes payment.collection.routes.ts

+
+ +
+ 64.51% + Statements + 20/31 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 64.51% + Lines + 20/31 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +1761x +1x +1x +  +  +  +1x +  +1x +1x +1x +1x +1x +1x +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +1x +1x +1x +1x +1x +  +1x + 
import express, { NextFunction, Request, Response, Router } from "express";
+import authCheck from "../middleware/auth-check";
+import {
+  collectionResponseAdwa,
+  mobilePaymentCollection,
+} from "../controllers/payment.collection.controller";
+import { z } from "zod";
+ 
+const paymentSchema = z.object({
+  meanCode: z.string().min(1),
+  paymentNumber: z.string().min(1),
+  currency: z.string().min(1),
+  feesAmount: z.number().min(0),
+  amount: z.number().min(1),
+});
+ 
+function validatePayment(req: Request, res: Response, next: NextFunction) {
+  const parsed = paymentSchema.safeParse({
+    ...req.body,
+    amount: req.body.amount ? Number(req.body.amount) : undefined,
+    feesAmount: req.body.feesAmount ? Number(req.body.feesAmount) : undefined,
+  });
+  if (!parsed.success) {
+    return res.status(400).json({ errors: parsed.error.flatten().fieldErrors });
+  }
+  next();
+}
+ 
+const paymentCollectionRoute: Router = express.Router();
+ 
+paymentCollectionRoute.post("/webhook/adwapay", collectionResponseAdwa);
+ 
+/**
+ * @swagger
+ * components:
+ *  securitySchemes:
+ *      bearerAuth:
+ *          type: http
+ *          scheme: bearer
+ *          bearerFormat: JWT
+ *  security:
+ *      - bearerAuth: []
+ */
+ 
+/**
+ * @swagger
+ * tags:
+ *   name: Payment Collection
+ *   description: API for managing user payment Collection
+ */
+ 
+/**
+ * @swagger
+ * components:
+ *   schemas:
+ *     PaymentRequest:
+ *       type: object
+ *       required:
+ *         - amount
+ *         - meanCode
+ *         - currency
+ *       properties:
+ *         amount:
+ *           type: string
+ *           description: The payment amount
+ *           example: "1000.00"
+ *         meanCode:
+ *           type: string
+ *           description: Payment method code (MASTERCARD, VISA, etc.)
+ *           example: "MASTERCARD"
+ *         currency:
+ *           type: string
+ *           description: Currency code
+ *           example: "XAF"
+ *         phoneNumber:
+ *           type: string
+ *           description: Customer phone number (required for mobile payments)
+ *           example: "237612345678"
+ *     PaymentResponse:
+ *       type: object
+ *       properties:
+ *         status:
+ *           type: string
+ *           description: Status of the payment operation
+ *           example: "success"
+ *         message:
+ *           type: object
+ *           description: Payment processing response
+ *           properties:
+ *             status:
+ *               type: string
+ *               example: "T"
+ *             transactionId:
+ *               type: string
+ *               example: "PAY123456789"
+ *             timestamp:
+ *               type: string
+ *               example: "2023-03-15T10:20:30Z"
+ */
+ 
+/**
+ * @swagger
+ * /user/transaction/{orderId}/paymentCollection/mobile:
+ *  post:
+ *      summary: Payment route for either mobile or card
+ *      tags: [Payment Collection]
+ *      security:
+ *          - bearerAuth: []
+ *      parameters:
+ *        - in: path
+ *          name: orderId
+ *          schema:
+ *              type: string
+ *          required: true
+ *      requestBody:
+ *          required: true
+ *          content:
+ *              application/json:
+ *                  schema:
+ *                      type: object
+ *                      properties:
+ *                          meanCode:
+ *                              type: string
+ *                          paymentNumber:
+ *                              type: string
+ *                          currency:
+ *                              type: string
+ *                          feesAmount:
+ *                              type: string
+ *                          amount:
+ *                              type: integer
+ *                      example:
+ *                          meanCode: MOBILE-MONEY or ORANGE-MONEY or VISA or MASTERCARD
+ *                          paymentNumber: 672151908
+ *                          currency: XAF
+ *                          amount: 200
+ *                          feesAmount: 0
+ *
+ *      responses:
+ *          200:
+ *              description: data object in the response contains the following example output which you may use. For visa or mastercard redirect the user the card pay link where the user will complete the transaction
+ *              content:
+ *                  application/json:
+ *                      schema:
+ *                          type: object
+ *                      examples:
+ *                          MOBILE-MONEY OR ORANGE-MONEY:
+ *                              value:
+ *                                  adpFootprint: GOSHENWATER_CDF412FBA4535
+ *                                  status: T
+ *                                  meanCode: MOBILE-MONEY,
+ *                                  orderNumber: order_a049662d-e152-4dae-8e30-c010cc95435e_1718824055475
+ *                          VISA or MASTERCARD:
+ *                              value:
+ *                                  adpFootprint: GOSHENWATER_56964554A5E4C
+ *                                  orderNumber: order_a049662d-e152-4dae-8e30-c010cc95435e_1718865202762
+ *                                  status: E
+ *                                  description: null
+ *                                  CARD_PAY_LINK: https://dev.adwa.world/ADPFO/BANK/ADWACARD?adpFootprint=GOSHENWATER_56964554A5E4C
+ *          403:
+ *              description: Forbiden
+ *          500:
+ *              description: Internal Server Error
+ *          429:
+ *              description: Too many requests
+ */
+ 
+paymentCollectionRoute.post(
+  "/:orderId/paymentCollection/mobile",
+  authCheck,
+  validatePayment,
+  mobilePaymentCollection,
+);
+ 
+export default paymentCollectionRoute;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/farming-product-REST-api/src/routes/product.routes.ts.html b/coverage/lcov-report/farming-product-REST-api/src/routes/product.routes.ts.html new file mode 100644 index 0000000..7eb8e31 --- /dev/null +++ b/coverage/lcov-report/farming-product-REST-api/src/routes/product.routes.ts.html @@ -0,0 +1,1183 @@ + + + + + + Code coverage report for farming-product-REST-api/src/routes/product.routes.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api/src/routes product.routes.ts

+
+ +
+ 73.33% + Statements + 33/45 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 73.33% + Lines + 33/45 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +335 +336 +337 +338 +339 +340 +341 +342 +343 +344 +345 +346 +347 +348 +349 +350 +351 +352 +353 +354 +355 +356 +357 +358 +359 +360 +361 +362 +363 +364 +365 +366 +3671x +1x +1x +1x +  +  +  +  +  +  +1x +1x +  +1x +1x +1x +1x +1x +1x +1x +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +1x +1x +1x +1x +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +1x +1x +1x +1x +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +1x + 
import express, { NextFunction, Request, Response, Router } from "express";
+import authCheck from "../middleware/auth-check";
+import uploadMiddleware from "../middleware/multerStorage";
+import {
+  getProduct,
+  createProduct,
+  updateProduct,
+  removeProduct,
+  userProducts,
+} from "../controllers/product.controller";
+import { getAllProductSearch } from "../controllers/product.search.controller";
+import { z } from "zod";
+ 
+const productSchema = z.object({
+  productName: z.string().min(1),
+  productCat: z.string().min(1),
+  priceType: z.string().min(1),
+  price: z.number().min(0),
+  description: z.string().optional(),
+  wholeSale: z.boolean().optional(),
+});
+ 
+function validateProduct(req: Request, res: Response, next: NextFunction) {
+  const data = req.body;
+  // If using multer, req.body fields may be strings, so coerce as needed
+  const parsed = productSchema.safeParse({
+    ...data,
+    price: data.price ? Number(data.price) : undefined,
+    wholeSale: data.wholeSale === "true" || data.wholeSale === true,
+  });
+  if (!parsed.success) {
+    return res.status(400).json({ errors: parsed.error.flatten().fieldErrors });
+  }
+  next();
+}
+ 
+const productRouter: Router = express.Router();
+ 
+productRouter.use(authCheck);
+ 
+/**
+ * @swagger
+ * components:
+ *  securitySchemes:
+ *      bearerAuth:
+ *          type: http
+ *          scheme: bearer
+ *          bearerFormat: JWT
+ *  security:
+ *      - bearerAuth: []
+ */
+ 
+/**
+ * @swagger
+ * tags:
+ *   name: Products
+ *   description: API for managing user products
+ */
+ 
+/**
+ * @swagger
+ * components:
+ *   schemas:
+ *     Product:
+ *       type: object
+ *       required:
+ *         - userId
+ *       properties:
+ *         id:
+ *           type: string
+ *           format: uuid
+ *           description: The auto-generated id of the product
+ *           example: 550e8400-e29b-41d4-a716-446655440000
+ *         productName:
+ *           type: string
+ *           description: Name of the product
+ *           example: Organic Fresh Tomatoes
+ *         productCat:
+ *           type: string
+ *           description: Category of the product
+ *           example: Vegetables
+ *         priceType:
+ *           type: string
+ *           description: Type of pricing (e.g., per kg, per piece)
+ *           example: per kg
+ *         price:
+ *           type: number
+ *           format: float
+ *           description: Price of the product
+ *           example: 1500
+ *         imageUrl:
+ *           type: string
+ *           description: URL to the product image
+ *           example: https://farmingproducts-storage.com/images/tomatoes.jpg
+ *         description:
+ *           type: string
+ *           description: Detailed description of the product
+ *           example: Fresh, locally grown organic tomatoes from our partner farms
+ *         wholeSale:
+ *           type: boolean
+ *           description: Indicates if the product is available for wholesale
+ *           example: false
+ *         userId:
+ *           type: string
+ *           format: uuid
+ *           description: ID of the user who created/owns this product
+ *           example: 550e8400-e29b-41d4-a716-446655440001
+ *         createdAt:
+ *           type: string
+ *           format: date-time
+ *           description: The date the product was created
+ *           example: 2023-03-15T12:00:00Z
+ *         updatedAt:
+ *           type: string
+ *           format: date-time
+ *           description: The date the product was last updated
+ *           example: 2023-03-16T14:30:00Z
+ */
+ 
+/**
+ * @swagger
+ * /user/product:
+ *  get:
+ *      summary: Search product using query parameters
+ *      tags: [Products]
+ *      security:
+ *          - bearerAuth: []
+ *      parameters:
+ *        - in: query
+ *          name: productName
+ *          schema:
+ *              type: string
+ *          required: false
+ *        - in: query
+ *          name: productCat
+ *          schema:
+ *              type: string
+ *          required: false
+ *        - in: query
+ *          name: page
+ *          schema:
+ *              type: integer
+ *          required: false
+ *        - in: query
+ *          name: limit
+ *          schema:
+ *              type: integer
+ *          required: false
+ *        - in: query
+ *          name: minPrice
+ *          schema:
+ *              type: integer
+ *          required: false
+ *        - in: query
+ *          name: maxPrice
+ *          schema:
+ *              type: integer
+ *          required: false
+ *      responses:
+ *          200:
+ *              description: Success. getting all products or products on the basis of search query
+ *          500:
+ *              description: Server Error
+ *          403:
+ *              description: Forbidden
+ *
+ */
+ 
+productRouter.get("/", getAllProductSearch);
+ 
+/**
+ * @swagger
+ * /user/product/{productId}:
+ *  get:
+ *      summary: getting a product based on its ID
+ *      tags: [Products]
+ *      security:
+ *          - bearerAuth: []
+ *      parameters:
+ *        - in: path
+ *          name: productId
+ *          schema:
+ *              type: string
+ *          required: true
+ *      responses:
+ *          200:
+ *              description: Ok. a product Data based on the product ID
+ *          403:
+ *              description: Forbiden
+ *          500:
+ *              description: Server Error
+ */
+productRouter.get("/:productId", getProduct);
+ 
+/**
+ * @swagger
+ * /user/product/{userId}/products:
+ *  get:
+ *      summary: all products of the user
+ *      tags: [Products]
+ *      security:
+ *          - bearerAuth: []
+ *      parameters:
+ *        - in: path
+ *          name: userId
+ *          schema:
+ *              type: string
+ *          required: true
+ *      responses:
+ *          200:
+ *              description: OK. products of a user
+ *          500:
+ *              description: Server Error
+ *          403:
+ *              description: Forbiden
+ *          429:
+ *              description: Too many Requests
+ */
+productRouter.get("/:userId/products", userProducts);
+ 
+/**
+ * @swagger
+ * /user/product/add:
+ *   post:
+ *     summary: Add a new product (with image upload)
+ *     tags: [Products]
+ *     security:
+ *       - bearerAuth: []
+ *     requestBody:
+ *       required: true
+ *       content:
+ *         multipart/form-data:
+ *           schema:
+ *             type: object
+ *             required:
+ *               - productName
+ *               - productCat
+ *               - priceType
+ *               - price
+ *               - productImage
+ *             properties:
+ *               productName:
+ *                 type: string
+ *               productCat:
+ *                 type: string
+ *               priceType:
+ *                 type: string
+ *               price:
+ *                 type: number
+ *               description:
+ *                 type: string
+ *               wholeSale:
+ *                 type: boolean
+ *               productImage:
+ *                 type: string
+ *                 format: binary
+ *     responses:
+ *       200:
+ *         description: Product created successfully
+ *       400:
+ *         description: Validation error
+ *         content:
+ *           application/json:
+ *             schema:
+ *               type: object
+ *               properties:
+ *                 errors:
+ *                   type: object
+ *                   example: { productName: ["Required"], price: ["Must be a number"] }
+ *       500:
+ *         description: Internal Server Error
+ */
+ 
+productRouter.post(
+  "/add",
+  uploadMiddleware("productImage"),
+  validateProduct,
+  createProduct,
+);
+ 
+/**
+ * @swagger
+ * /user/product/update/{productId}:
+ *   put:
+ *     summary: Update product fields (with image upload)
+ *     tags: [Products]
+ *     security:
+ *       - bearerAuth: []
+ *     parameters:
+ *       - in: path
+ *         name: productId
+ *         schema:
+ *           type: string
+ *         required: true
+ *     requestBody:
+ *       required: true
+ *       content:
+ *         multipart/form-data:
+ *           schema:
+ *             type: object
+ *             properties:
+ *               productName:
+ *                 type: string
+ *               productCat:
+ *                 type: string
+ *               priceType:
+ *                 type: string
+ *               price:
+ *                 type: number
+ *               description:
+ *                 type: string
+ *               wholeSale:
+ *                 type: boolean
+ *               productImage:
+ *                 type: string
+ *                 format: binary
+ *     responses:
+ *       200:
+ *         description: Product updated successfully
+ *       400:
+ *         description: Validation error
+ *         content:
+ *           application/json:
+ *             schema:
+ *               type: object
+ *               properties:
+ *                 errors:
+ *                   type: object
+ *                   example: { productName: ["Required"], price: ["Must be a number"] }
+ *       500:
+ *         description: Internal Server Error
+ */
+productRouter.put(
+  "/:productId",
+  uploadMiddleware("productImage"),
+  validateProduct,
+  updateProduct,
+);
+ 
+/**
+ * @swagger
+ * /user/product/remove/{productId}:
+ *  delete:
+ *      summary: Caution! removing a product by the user
+ *      tags: [Products]
+ *      security:
+ *          - bearerAuth: []
+ *      parameters:
+ *        - in: path
+ *          name: productId
+ *          required: true
+ *          schema:
+ *              type: string
+ *      responses:
+ *          200:
+ *              description: OK
+ *          403:
+ *              description: Forbiden
+ *          500:
+ *              description: Internal Server Error
+ *          429:
+ *              description: Too many requests
+ */
+productRouter.delete("/:productId", removeProduct);
+ 
+export default productRouter;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/farming-product-REST-api/src/routes/review.routes.ts.html b/coverage/lcov-report/farming-product-REST-api/src/routes/review.routes.ts.html new file mode 100644 index 0000000..f91f071 --- /dev/null +++ b/coverage/lcov-report/farming-product-REST-api/src/routes/review.routes.ts.html @@ -0,0 +1,910 @@ + + + + + + Code coverage report for farming-product-REST-api/src/routes/review.routes.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api/src/routes review.routes.ts

+
+ +
+ 60% + Statements + 15/25 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 60% + Lines + 15/25 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +2761x +1x +1x +  +  +  +  +  +  +1x +  +1x +1x +1x +1x +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +1x + 
import express, { NextFunction, Request, Response, Router } from "express";
+import authCheck from "../middleware/auth-check";
+import {
+  getReviewByProdId,
+  createReview,
+  updateReview,
+  deleteOwnReview,
+  orderReview,
+} from "../controllers/review.controller";
+import { z } from "zod";
+ 
+const reviewSchema = z.object({
+  comment: z.string().min(1),
+  rating: z.number().min(1).max(5),
+});
+ 
+function validateReview(req: Request, res: Response, next: NextFunction) {
+  const parsed = reviewSchema.safeParse({
+    ...req.body,
+    rating: req.body.rating ? Number(req.body.rating) : undefined,
+  });
+  if (!parsed.success) {
+    return res.status(400).json({ errors: parsed.error.flatten().fieldErrors });
+  }
+  next();
+}
+ 
+const reviewRouter: Router = express.Router();
+ 
+// reviewRouter.get('/:userId')
+ 
+/**
+ * @swagger
+ * components:
+ *  securitySchemes:
+ *      bearerAuth:
+ *          type: http
+ *          scheme: bearer
+ *          bearerFormat: JWT
+ *  security:
+ *      - bearerAuth: []
+ */
+ 
+/**
+ * @swagger
+ * tags:
+ *   name: Reviews
+ *   description: API for managing user reviews
+ */
+ 
+/**
+ * @swagger
+ * components:
+ *   schemas:
+ *     BuyerReview:
+ *       type: object
+ *       required:
+ *         - comment
+ *         - rating
+ *         - userId
+ *         - prodId
+ *         - orderId
+ *       properties:
+ *         id:
+ *           type: string
+ *           format: uuid
+ *           description: The auto-generated id of the review
+ *           example: 550e8400-e29b-41d4-a716-446655440000
+ *         comment:
+ *           type: string
+ *           description: The review comment text
+ *           example: The product was fresh and delivered on time. Excellent quality!
+ *         rating:
+ *           type: number
+ *           minimum: 1
+ *           maximum: 5
+ *           description: Rating given by the buyer (typically 1-5)
+ *           example: 4.5
+ *         userId:
+ *           type: string
+ *           format: uuid
+ *           description: ID of the user who created this review
+ *           example: 550e8400-e29b-41d4-a716-446655440001
+ *         prodId:
+ *           type: string
+ *           format: uuid
+ *           description: ID of the product being reviewed
+ *           example: 550e8400-e29b-41d4-a716-446655440002
+ *         orderId:
+ *           type: string
+ *           format: uuid
+ *           description: ID of the order associated with this review
+ *           example: 550e8400-e29b-41d4-a716-446655440003
+ *         createdAt:
+ *           type: string
+ *           format: date-time
+ *           description: The date the review was created
+ *           example: 2023-03-15T12:00:00Z
+ *         updatedAt:
+ *           type: string
+ *           format: date-time
+ *           description: The date the review was last updated
+ *           example: 2023-03-16T14:30:00Z
+ */
+ 
+/**
+ * @swagger
+ * /user/review/{orderId}:
+ *  get:
+ *      summary: get a review of an order
+ *      tags: [Reviews]
+ *      security:
+ *          - bearerAuth: []
+ *      parameters:
+ *        - in: path
+ *          name: orderId
+ *          schema:
+ *              type: string
+ *          required: true
+ *      responses:
+ *          200:
+ *              description: OK
+ *          403:
+ *              description: Forbiden
+ *          500:
+ *              description: Internal Server Error
+ *          429:
+ *              description: Too many requests
+ */
+ 
+// get review of an order
+reviewRouter.get("/:orderId", authCheck, orderReview);
+ 
+/**
+ * @swagger
+ * /user/review/{productId}:
+ *  get:
+ *      summary: All reviews of a product
+ *      tags: [Reviews]
+ *      security:
+ *          - bearerAuth: []
+ *      parameters:
+ *        - in: path
+ *          name: productId
+ *          schema:
+ *              type: string
+ *          required: true
+ *        - in: query
+ *          name: rating
+ *          schema:
+ *              type: string
+ *          required: false
+ *      responses:
+ *          200:
+ *              description: OK
+ *          403:
+ *              description: Forbiden
+ *          500:
+ *              description: Internal Server Error
+ *          429:
+ *              description: Too many requests
+ */
+ 
+reviewRouter.get("/:productId", authCheck, getReviewByProdId);
+ 
+/**
+ * @swagger
+ * /user/review/{productId}/${orderId}/add:
+ *  post:
+ *      summary: Add review to a product
+ *      tags: [Reviews]
+ *      security:
+ *          - bearerAuth: []
+ *      parameters:
+ *        - in: path
+ *          name: productId
+ *          schema:
+ *              type: string
+ *          required: true
+ *        - in: path
+ *          name: orderId
+ *          schema:
+ *              type: string
+ *          required: true
+ *      requestBody:
+ *          required: true
+ *          content:
+ *              application/json:
+ *                  schema:
+ *                      type: object
+ *                      properties:
+ *                          comment:
+ *                              type: string
+ *                          rating:
+ *                              type: integer
+ *
+ *      responses:
+ *          200:
+ *              description: OK
+ *          403:
+ *              description: Forbiden
+ *          500:
+ *              description: Internal Server Error
+ *          429:
+ *              description: Too many requests
+ */
+ 
+reviewRouter.post("/:productId/:orderId/add", validateReview, createReview);
+ 
+/**
+ * @swagger
+ * /user/review/{reviewId}/update:
+ *  put:
+ *      summary: Update review to a product
+ *      tags: [Reviews]
+ *      security:
+ *          - bearerAuth: []
+ *      parameters:
+ *        - in: path
+ *          name: reviewId
+ *          schema:
+ *              type: string
+ *          required: true
+ *      requestBody:
+ *          required: true
+ *          content:
+ *              application/json:
+ *                  schema:
+ *                      type: object
+ *                      properties:
+ *                          comment:
+ *                              type: string
+ *                          rating:
+ *                              type: integer
+ *
+ *      responses:
+ *          200:
+ *              description: OK
+ *          403:
+ *              description: Forbiden
+ *          500:
+ *              description: Internal Server Error
+ *          429:
+ *              description: Too many requests
+ */
+reviewRouter.put("/:reviewId/update", validateReview, updateReview);
+ 
+/**
+ * @swagger
+ * /user/review/{reviewId}/remove:
+ *  delete:
+ *      summary: Delete review to a product
+ *      tags: [Reviews]
+ *      security:
+ *          - bearerAuth: []
+ *      parameters:
+ *        - in: path
+ *          name: reviewId
+ *          schema:
+ *              type: string
+ *          required: true
+ *
+ *      responses:
+ *          200:
+ *              description: OK
+ *          403:
+ *              description: Forbiden
+ *          500:
+ *              description: Internal Server Error
+ *          429:
+ *              description: Too many requests
+ */
+reviewRouter.delete("/:reviewId/remove", authCheck, deleteOwnReview);
+ 
+export default reviewRouter;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/farming-product-REST-api/src/routes/user.routes.ts.html b/coverage/lcov-report/farming-product-REST-api/src/routes/user.routes.ts.html new file mode 100644 index 0000000..af43749 --- /dev/null +++ b/coverage/lcov-report/farming-product-REST-api/src/routes/user.routes.ts.html @@ -0,0 +1,1174 @@ + + + + + + Code coverage report for farming-product-REST-api/src/routes/user.routes.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api/src/routes user.routes.ts

+
+ +
+ 63.33% + Statements + 19/30 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 63.33% + Lines + 19/30 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +335 +336 +337 +338 +339 +340 +341 +342 +343 +344 +345 +346 +347 +348 +349 +350 +351 +352 +353 +354 +355 +356 +357 +358 +359 +360 +361 +362 +363 +3641x +1x +1x +  +  +  +  +  +  +  +  +1x +1x +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +1x +1x +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +1x + 
import express, { Router, Request, Response, NextFunction } from "express";
+import authCheck from "../middleware/auth-check";
+import {
+  getUserData,
+  updateUser,
+  deleteUser,
+  updatePassword,
+  updateShipAddress,
+  addExpoPushNotificationToken,
+  getAllUserData,
+} from "../controllers/user.controller";
+import uploadMiddleware from "../middleware/multerStorage";
+import { z } from "zod";
+ 
+const userRouter: Router = express.Router();
+ 
+/**
+ * @swagger
+ * tags:
+ *   name: User profile
+ *   description: API for managing user profile
+ */
+ 
+/**
+ * @swagger
+ * components:
+ *   schemas:
+ *     User:
+ *       type: object
+ *       required:
+ *         - firstName
+ *         - lastName
+ *         - email
+ *         - address
+ *         - shipAddress
+ *         - country
+ *         - imageUrl
+ *         - googleId
+ *         - facebookId
+ *         - phoneNum
+ *         - roleId
+ *         - vip
+ *         - verifiedUser
+ *       properties:
+ *         id:
+ *           type: string
+ *           format: uuid
+ *           description: The auto-generated id of the user
+ *           example: 550e8400-e29b-41d4-a716-446655440000
+ *         firstName:
+ *           type: string
+ *           description: User's first name
+ *           example: John
+ *         lastName:
+ *           type: string
+ *           description: User's last name
+ *           example: Doe
+ *         email:
+ *           type: string
+ *           format: email
+ *           description: User's email address
+ *           example: john.doe@example.com
+ *         address:
+ *           type: string
+ *           description: User's primary address
+ *           example: 123 Main Street, Douala
+ *         shipAddress:
+ *           type: array
+ *           description: List of user's shipping addresses
+ *           items:
+ *             type: object
+ *             properties:
+ *               address:
+ *                 type: string
+ *                 example: 456 Shipping Ave, Yaounde
+ *               isDefault:
+ *                 type: boolean
+ *                 example: true
+ *         country:
+ *           type: string
+ *           description: User's country
+ *           example: Cameroon
+ *         imageUrl:
+ *           type: string
+ *           description: URL to user's profile image
+ *           example: https://farmingproducts-storage.com/profiles/john-doe.jpg
+ *         googleId:
+ *           type: string
+ *           description: Google OAuth ID (if user signed up with Google)
+ *           example: 118234546580958341234
+ *         facebookId:
+ *           type: string
+ *           description: Facebook OAuth ID (if user signed up with Facebook)
+ *           example: 10229876543210987
+ *         phoneNum:
+ *           type: number
+ *           description: User's phone number
+ *           example: 237612345678
+ *         roleId:
+ *           type: string
+ *           format: uuid
+ *           description: ID of the user's role
+ *           example: 550e8400-e29b-41d4-a716-446655440001
+ *         password:
+ *           type: string
+ *           format: password
+ *           description: User's password (hashed)
+ *           example: null
+ *         expoPushToken:
+ *           type: string
+ *           description: Token for push notifications
+ *           example: ExponentPushToken[xxxxxxxxxxxxxxxxxxxxxx]
+ *         vip:
+ *           type: boolean
+ *           description: Indicates if the user has VIP status
+ *           example: false
+ *         verifiedUser:
+ *           type: boolean
+ *           description: Indicates if the user's identity has been verified
+ *           example: true
+ *         createdAt:
+ *           type: string
+ *           format: date-time
+ *           description: The date the user was created
+ *           example: 2023-03-15T12:00:00Z
+ *         updatedAt:
+ *           type: string
+ *           format: date-time
+ *           description: The date the user was last updated
+ *           example: 2023-03-16T14:30:00Z
+ */
+ 
+// Zod schema for updatePassword endpoint
+const updatePasswordSchema = z.object({
+  password: z.string().min(8, "Password must be at least 8 characters"),
+  userId: z.string().uuid("Invalid userId format"),
+});
+ 
+function validateUpdatePassword(
+  req: Request,
+  res: Response,
+  next: NextFunction,
+) {
+  const result = updatePasswordSchema.safeParse(req.body);
+  if (!result.success) {
+    return res.status(400).json({ errors: result.error.flatten().fieldErrors });
+  }
+  next();
+}
+ 
+// update user password
+ 
+/**
+ * @swagger
+ * /user/updatePassword/:
+ *  put:
+ *      summary: api to update user password
+ *      tags: [User profile]
+ *      requestBody:
+ *          required: true
+ *          content:
+ *              application/json:
+ *                  schema:
+ *                      type: object
+ *                      properties:
+ *                          password:
+ *                              type: string
+ *                          userId:
+ *                              type: string
+ *      responses:
+ *          200:
+ *              description: OK
+ *          500:
+ *              description: Server Error
+ *
+ */
+userRouter.put("/updatePassword/", validateUpdatePassword, updatePassword);
+ 
+// apply the auth middleware to all the routes below
+userRouter.use(authCheck);
+ 
+/**
+ * @swagger
+ * components:
+ *  securitySchemes:
+ *      bearerAuth:
+ *          type: http
+ *          scheme: bearer
+ *          bearerFormat: JWT
+ *  security:
+ *      - bearerAuth: []
+ */
+ 
+/**
+ * @swagger
+ * /user:
+ *  get:
+ *      summary: get all users
+ *      tags: [User profile]
+ *      security:
+ *          - bearerAuth: []
+ *      responses:
+ *          200:
+ *              description: OK
+ *          500:
+ *              description: server error
+ */
+ 
+userRouter.get("/", getAllUserData);
+ 
+/**
+ * @swagger
+ * /user/{userId}:
+ *  get:
+ *      summary: get logged a user data
+ *      tags: [User profile]
+ *      security:
+ *          - bearerAuth: []
+ *      responses:
+ *          200:
+ *              description: OK
+ *          500:
+ *              description: server error
+ */
+ 
+userRouter.get("/:userId", getUserData);
+ 
+/**
+ * @swagger
+ * /user/{userId}:
+ *   put:
+ *     summary: Update user profile (with image upload)
+ *     tags: [User profile]
+ *     security:
+ *       - bearerAuth: []
+ *     parameters:
+ *       - in: path
+ *         name: userId
+ *         schema:
+ *           type: string
+ *         required: true
+ *     requestBody:
+ *       required: true
+ *       content:
+ *         multipart/form-data:
+ *           schema:
+ *             type: object
+ *             properties:
+ *               firstName:
+ *                 type: string
+ *               lastName:
+ *                 type: string
+ *               address:
+ *                 type: string
+ *               profileImage:
+ *                 type: string
+ *                 format: binary
+ *     responses:
+ *       200:
+ *         description: User updated successfully
+ *       400:
+ *         description: Validation error
+ *         content:
+ *           application/json:
+ *             schema:
+ *               type: object
+ *               properties:
+ *                 errors:
+ *                   type: object
+ *                   example: { firstName: ["Required"] }
+ *       500:
+ *         description: Internal Server Error
+ */
+userRouter.put("/:userId", uploadMiddleware("profileImage"), updateUser);
+ 
+/**
+ * @swagger
+ * /user/{userId}/shipAddress:
+ *  put:
+ *      summary: api to update user shipping address
+ *      tags: [User profile]
+ *      security:
+ *          - bearerAuth: []
+ *      parameters:
+ *        - in: path
+ *          name: userId
+ *          schema:
+ *              type: string
+ *          required: true
+ *      requestBody:
+ *          required: true
+ *          content:
+ *            application/json:
+ *                  schema:
+ *                      type: object
+ *                      properties:
+ *                          shipAddress:
+ *                              type: string
+ *
+ *      responses:
+ *          200:
+ *              description: OK
+ *          500:
+ *              description: Server Error
+ *
+ */
+userRouter.put("/:userId/shipAddress", updateShipAddress);
+/**
+ * @swagger
+ * /user/{userId}/expoPushToken:
+ *  put:
+ *      summary: api to update user data via expo push token
+ *      tags: [User profile]
+ *      security:
+ *          - bearerAuth: []
+ *      parameters:
+ *        - in: path
+ *          name: userId
+ *          schema:
+ *              type: string
+ *          required: true
+ *      requestBody:
+ *          required: true
+ *          content:
+ *             application/json:
+ *                  schema:
+ *                      type: object
+ *                      properties:
+ *                          expoPushToken:
+ *                              type: string
+ *
+ *      responses:
+ *          200:
+ *              description: OK
+ *          500:
+ *              description: Server Error
+ *
+ */
+userRouter.put("/:userId/expoPushToken", addExpoPushNotificationToken);
+ 
+/**
+ * @swagger
+ * /user/{userId}:
+ *  delete:
+ *      summary: Caution!! Deleting Profile
+ *      tags: [User profile]
+ *      security:
+ *          - bearerAuth: []
+ *      parameters:
+ *        - in: path
+ *          name: userId
+ *          schema:
+ *              type: string
+ *          required: true
+ *      responses:
+ *          200:
+ *              description: OK
+ *          500:
+ *              description: Server error
+ */
+userRouter.delete("/:userId", deleteUser);
+ 
+export default userRouter;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/farming-product-REST-api/src/utils/index.html b/coverage/lcov-report/farming-product-REST-api/src/utils/index.html new file mode 100644 index 0000000..a3bd189 --- /dev/null +++ b/coverage/lcov-report/farming-product-REST-api/src/utils/index.html @@ -0,0 +1,116 @@ + + + + + + Code coverage report for farming-product-REST-api/src/utils + + + + + + + + + +
+
+

All files farming-product-REST-api/src/utils

+
+ +
+ 0% + Statements + 0/19 +
+ + +
+ 100% + Branches + 1/1 +
+ + +
+ 100% + Functions + 1/1 +
+ + +
+ 0% + Lines + 0/19 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
runMigrations.ts +
+
0%0/19100%1/1100%1/10%0/19
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/farming-product-REST-api/src/utils/runMigrations.ts.html b/coverage/lcov-report/farming-product-REST-api/src/utils/runMigrations.ts.html new file mode 100644 index 0000000..5690d39 --- /dev/null +++ b/coverage/lcov-report/farming-product-REST-api/src/utils/runMigrations.ts.html @@ -0,0 +1,169 @@ + + + + + + Code coverage report for farming-product-REST-api/src/utils/runMigrations.ts + + + + + + + + + +
+
+

All files / farming-product-REST-api/src/utils runMigrations.ts

+
+ +
+ 0% + Statements + 0/19 +
+ + +
+ 100% + Branches + 1/1 +
+ + +
+ 100% + Functions + 1/1 +
+ + +
+ 0% + Lines + 0/19 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import { Sequelize } from "sequelize-typescript";
+import { Umzug, SequelizeStorage } from "umzug";
+ 
+/**
+ * Run database migrations
+ * @param sequelize - Sequelize instance
+ * @returns Promise<void>
+ */
+const runMigrations = async (sequelize: Sequelize): Promise<void> => {
+  const umzug = new Umzug({
+    migrations: {
+      glob: ["migrations/*.js", { cwd: process.cwd() }],
+    },
+    context: sequelize.getQueryInterface(),
+    storage: new SequelizeStorage({ sequelize }),
+    logger: console,
+  });
+ 
+  try {
+    await umzug.up();
+    console.log("✅ All migrations have been executed successfully.");
+  } catch (error) {
+    console.error("❌ Error running migrations:", error);
+    throw error;
+  }
+};
+ 
+export default runMigrations;
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/favicon.png b/coverage/lcov-report/favicon.png new file mode 100644 index 0000000..c1525b8 Binary files /dev/null and b/coverage/lcov-report/favicon.png differ diff --git a/coverage/lcov-report/index.html b/coverage/lcov-report/index.html new file mode 100644 index 0000000..41c0c50 --- /dev/null +++ b/coverage/lcov-report/index.html @@ -0,0 +1,221 @@ + + + + + + Code coverage report for All files + + + + + + + + + +
+
+

All files

+
+ +
+ 27.05% + Statements + 950/3511 +
+ + +
+ 53.65% + Branches + 22/41 +
+ + +
+ 11.47% + Functions + 7/61 +
+ + +
+ 27.05% + Lines + 950/3511 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
farming-product-REST-api +
+
88.07%96/10925%1/4100%0/088.07%96/109
farming-product-REST-api/src/config +
+
0%0/250%0/10%0/10%0/25
farming-product-REST-api/src/controllers +
+
7.8%177/226945%9/204.87%2/417.8%177/2269
farming-product-REST-api/src/errors +
+
100%10/10100%3/3100%1/1100%10/10
farming-product-REST-api/src/middleware +
+
26.07%67/25750%3/630%3/1026.07%67/257
farming-product-REST-api/src/models +
+
73.21%440/60183.33%5/60%0/173.21%440/601
farming-product-REST-api/src/routes +
+
72.39%160/221100%0/00%0/672.39%160/221
farming-product-REST-api/src/utils +
+
0%0/19100%1/1100%1/10%0/19
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/prettify.css b/coverage/lcov-report/prettify.css new file mode 100644 index 0000000..b317a7c --- /dev/null +++ b/coverage/lcov-report/prettify.css @@ -0,0 +1 @@ +.pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee} diff --git a/coverage/lcov-report/prettify.js b/coverage/lcov-report/prettify.js new file mode 100644 index 0000000..b322523 --- /dev/null +++ b/coverage/lcov-report/prettify.js @@ -0,0 +1,2 @@ +/* eslint-disable */ +window.PR_SHOULD_USE_CONTINUATION=true;(function(){var h=["break,continue,do,else,for,if,return,while"];var u=[h,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"];var p=[u,"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"];var l=[p,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"];var x=[p,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"];var R=[x,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"];var r="all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes";var w=[p,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"];var s="caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END";var I=[h,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"];var f=[h,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"];var H=[h,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"];var A=[l,R,w,s+I,f,H];var e=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/;var C="str";var z="kwd";var j="com";var O="typ";var G="lit";var L="pun";var F="pln";var m="tag";var E="dec";var J="src";var P="atn";var n="atv";var N="nocode";var M="(?:^^\\.?|[+-]|\\!|\\!=|\\!==|\\#|\\%|\\%=|&|&&|&&=|&=|\\(|\\*|\\*=|\\+=|\\,|\\-=|\\->|\\/|\\/=|:|::|\\;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|\\?|\\@|\\[|\\^|\\^=|\\^\\^|\\^\\^=|\\{|\\||\\|=|\\|\\||\\|\\|=|\\~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*";function k(Z){var ad=0;var S=false;var ac=false;for(var V=0,U=Z.length;V122)){if(!(al<65||ag>90)){af.push([Math.max(65,ag)|32,Math.min(al,90)|32])}if(!(al<97||ag>122)){af.push([Math.max(97,ag)&~32,Math.min(al,122)&~32])}}}}af.sort(function(av,au){return(av[0]-au[0])||(au[1]-av[1])});var ai=[];var ap=[NaN,NaN];for(var ar=0;arat[0]){if(at[1]+1>at[0]){an.push("-")}an.push(T(at[1]))}}an.push("]");return an.join("")}function W(al){var aj=al.source.match(new RegExp("(?:\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]|\\\\u[A-Fa-f0-9]{4}|\\\\x[A-Fa-f0-9]{2}|\\\\[0-9]+|\\\\[^ux0-9]|\\(\\?[:!=]|[\\(\\)\\^]|[^\\x5B\\x5C\\(\\)\\^]+)","g"));var ah=aj.length;var an=[];for(var ak=0,am=0;ak=2&&ai==="["){aj[ak]=X(ag)}else{if(ai!=="\\"){aj[ak]=ag.replace(/[a-zA-Z]/g,function(ao){var ap=ao.charCodeAt(0);return"["+String.fromCharCode(ap&~32,ap|32)+"]"})}}}}return aj.join("")}var aa=[];for(var V=0,U=Z.length;V=0;){S[ac.charAt(ae)]=Y}}var af=Y[1];var aa=""+af;if(!ag.hasOwnProperty(aa)){ah.push(af);ag[aa]=null}}ah.push(/[\0-\uffff]/);V=k(ah)})();var X=T.length;var W=function(ah){var Z=ah.sourceCode,Y=ah.basePos;var ad=[Y,F];var af=0;var an=Z.match(V)||[];var aj={};for(var ae=0,aq=an.length;ae=5&&"lang-"===ap.substring(0,5);if(am&&!(ai&&typeof ai[1]==="string")){am=false;ap=J}if(!am){aj[ag]=ap}}var ab=af;af+=ag.length;if(!am){ad.push(Y+ab,ap)}else{var al=ai[1];var ak=ag.indexOf(al);var ac=ak+al.length;if(ai[2]){ac=ag.length-ai[2].length;ak=ac-al.length}var ar=ap.substring(5);B(Y+ab,ag.substring(0,ak),W,ad);B(Y+ab+ak,al,q(ar,al),ad);B(Y+ab+ac,ag.substring(ac),W,ad)}}ah.decorations=ad};return W}function i(T){var W=[],S=[];if(T.tripleQuotedStrings){W.push([C,/^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/,null,"'\""])}else{if(T.multiLineStrings){W.push([C,/^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,null,"'\"`"])}else{W.push([C,/^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,null,"\"'"])}}if(T.verbatimStrings){S.push([C,/^@\"(?:[^\"]|\"\")*(?:\"|$)/,null])}var Y=T.hashComments;if(Y){if(T.cStyleComments){if(Y>1){W.push([j,/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,null,"#"])}else{W.push([j,/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\r\n]*)/,null,"#"])}S.push([C,/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,null])}else{W.push([j,/^#[^\r\n]*/,null,"#"])}}if(T.cStyleComments){S.push([j,/^\/\/[^\r\n]*/,null]);S.push([j,/^\/\*[\s\S]*?(?:\*\/|$)/,null])}if(T.regexLiterals){var X=("/(?=[^/*])(?:[^/\\x5B\\x5C]|\\x5C[\\s\\S]|\\x5B(?:[^\\x5C\\x5D]|\\x5C[\\s\\S])*(?:\\x5D|$))+/");S.push(["lang-regex",new RegExp("^"+M+"("+X+")")])}var V=T.types;if(V){S.push([O,V])}var U=(""+T.keywords).replace(/^ | $/g,"");if(U.length){S.push([z,new RegExp("^(?:"+U.replace(/[\s,]+/g,"|")+")\\b"),null])}W.push([F,/^\s+/,null," \r\n\t\xA0"]);S.push([G,/^@[a-z_$][a-z_$@0-9]*/i,null],[O,/^(?:[@_]?[A-Z]+[a-z][A-Za-z_$@0-9]*|\w+_t\b)/,null],[F,/^[a-z_$][a-z_$@0-9]*/i,null],[G,new RegExp("^(?:0x[a-f0-9]+|(?:\\d(?:_\\d+)*\\d*(?:\\.\\d*)?|\\.\\d\\+)(?:e[+\\-]?\\d+)?)[a-z]*","i"),null,"0123456789"],[F,/^\\[\s\S]?/,null],[L,/^.[^\s\w\.$@\'\"\`\/\#\\]*/,null]);return g(W,S)}var K=i({keywords:A,hashComments:true,cStyleComments:true,multiLineStrings:true,regexLiterals:true});function Q(V,ag){var U=/(?:^|\s)nocode(?:\s|$)/;var ab=/\r\n?|\n/;var ac=V.ownerDocument;var S;if(V.currentStyle){S=V.currentStyle.whiteSpace}else{if(window.getComputedStyle){S=ac.defaultView.getComputedStyle(V,null).getPropertyValue("white-space")}}var Z=S&&"pre"===S.substring(0,3);var af=ac.createElement("LI");while(V.firstChild){af.appendChild(V.firstChild)}var W=[af];function ae(al){switch(al.nodeType){case 1:if(U.test(al.className)){break}if("BR"===al.nodeName){ad(al);if(al.parentNode){al.parentNode.removeChild(al)}}else{for(var an=al.firstChild;an;an=an.nextSibling){ae(an)}}break;case 3:case 4:if(Z){var am=al.nodeValue;var aj=am.match(ab);if(aj){var ai=am.substring(0,aj.index);al.nodeValue=ai;var ah=am.substring(aj.index+aj[0].length);if(ah){var ak=al.parentNode;ak.insertBefore(ac.createTextNode(ah),al.nextSibling)}ad(al);if(!ai){al.parentNode.removeChild(al)}}}break}}function ad(ak){while(!ak.nextSibling){ak=ak.parentNode;if(!ak){return}}function ai(al,ar){var aq=ar?al.cloneNode(false):al;var ao=al.parentNode;if(ao){var ap=ai(ao,1);var an=al.nextSibling;ap.appendChild(aq);for(var am=an;am;am=an){an=am.nextSibling;ap.appendChild(am)}}return aq}var ah=ai(ak.nextSibling,0);for(var aj;(aj=ah.parentNode)&&aj.nodeType===1;){ah=aj}W.push(ah)}for(var Y=0;Y=S){ah+=2}if(V>=ap){Z+=2}}}var t={};function c(U,V){for(var S=V.length;--S>=0;){var T=V[S];if(!t.hasOwnProperty(T)){t[T]=U}else{if(window.console){console.warn("cannot override language handler %s",T)}}}}function q(T,S){if(!(T&&t.hasOwnProperty(T))){T=/^\s*]*(?:>|$)/],[j,/^<\!--[\s\S]*?(?:-\->|$)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],[L,/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\s\S]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]),["default-markup","htm","html","mxml","xhtml","xml","xsl"]);c(g([[F,/^[\s]+/,null," \t\r\n"],[n,/^(?:\"[^\"]*\"?|\'[^\']*\'?)/,null,"\"'"]],[[m,/^^<\/?[a-z](?:[\w.:-]*\w)?|\/?>$/i],[P,/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^>\'\"\s]*(?:[^>\'\"\s\/]|\/(?=\s)))/],[L,/^[=<>\/]+/],["lang-js",/^on\w+\s*=\s*\"([^\"]+)\"/i],["lang-js",/^on\w+\s*=\s*\'([^\']+)\'/i],["lang-js",/^on\w+\s*=\s*([^\"\'>\s]+)/i],["lang-css",/^style\s*=\s*\"([^\"]+)\"/i],["lang-css",/^style\s*=\s*\'([^\']+)\'/i],["lang-css",/^style\s*=\s*([^\"\'>\s]+)/i]]),["in.tag"]);c(g([],[[n,/^[\s\S]+/]]),["uq.val"]);c(i({keywords:l,hashComments:true,cStyleComments:true,types:e}),["c","cc","cpp","cxx","cyc","m"]);c(i({keywords:"null,true,false"}),["json"]);c(i({keywords:R,hashComments:true,cStyleComments:true,verbatimStrings:true,types:e}),["cs"]);c(i({keywords:x,cStyleComments:true}),["java"]);c(i({keywords:H,hashComments:true,multiLineStrings:true}),["bsh","csh","sh"]);c(i({keywords:I,hashComments:true,multiLineStrings:true,tripleQuotedStrings:true}),["cv","py"]);c(i({keywords:s,hashComments:true,multiLineStrings:true,regexLiterals:true}),["perl","pl","pm"]);c(i({keywords:f,hashComments:true,multiLineStrings:true,regexLiterals:true}),["rb"]);c(i({keywords:w,cStyleComments:true,regexLiterals:true}),["js"]);c(i({keywords:r,hashComments:3,cStyleComments:true,multilineStrings:true,tripleQuotedStrings:true,regexLiterals:true}),["coffee"]);c(g([],[[C,/^[\s\S]+/]]),["regex"]);function d(V){var U=V.langExtension;try{var S=a(V.sourceNode);var T=S.sourceCode;V.sourceCode=T;V.spans=S.spans;V.basePos=0;q(U,T)(V);D(V)}catch(W){if("console" in window){console.log(W&&W.stack?W.stack:W)}}}function y(W,V,U){var S=document.createElement("PRE");S.innerHTML=W;if(U){Q(S,U)}var T={langExtension:V,numberLines:U,sourceNode:S};d(T);return S.innerHTML}function b(ad){function Y(af){return document.getElementsByTagName(af)}var ac=[Y("pre"),Y("code"),Y("xmp")];var T=[];for(var aa=0;aa=0){var ah=ai.match(ab);var am;if(!ah&&(am=o(aj))&&"CODE"===am.tagName){ah=am.className.match(ab)}if(ah){ah=ah[1]}var al=false;for(var ak=aj.parentNode;ak;ak=ak.parentNode){if((ak.tagName==="pre"||ak.tagName==="code"||ak.tagName==="xmp")&&ak.className&&ak.className.indexOf("prettyprint")>=0){al=true;break}}if(!al){var af=aj.className.match(/\blinenums\b(?::(\d+))?/);af=af?af[1]&&af[1].length?+af[1]:true:false;if(af){Q(aj,af)}S={langExtension:ah,sourceNode:aj,numberLines:af};d(S)}}}if(X]*(?:>|$)/],[PR.PR_COMMENT,/^<\!--[\s\S]*?(?:-\->|$)/],[PR.PR_PUNCTUATION,/^(?:<[%?]|[%?]>)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],["lang-",/^]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-handlebars",/^]*type\s*=\s*['"]?text\/x-handlebars-template['"]?\b[^>]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-js",/^]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\s\S]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i],[PR.PR_DECLARATION,/^{{[#^>/]?\s*[\w.][^}]*}}/],[PR.PR_DECLARATION,/^{{&?\s*[\w.][^}]*}}/],[PR.PR_DECLARATION,/^{{{>?\s*[\w.][^}]*}}}/],[PR.PR_COMMENT,/^{{![^}]*}}/]]),["handlebars","hbs"]);PR.registerLangHandler(PR.createSimpleLexer([[PR.PR_PLAIN,/^[ \t\r\n\f]+/,null," \t\r\n\f"]],[[PR.PR_STRING,/^\"(?:[^\n\r\f\\\"]|\\(?:\r\n?|\n|\f)|\\[\s\S])*\"/,null],[PR.PR_STRING,/^\'(?:[^\n\r\f\\\']|\\(?:\r\n?|\n|\f)|\\[\s\S])*\'/,null],["lang-css-str",/^url\(([^\)\"\']*)\)/i],[PR.PR_KEYWORD,/^(?:url|rgb|\!important|@import|@page|@media|@charset|inherit)(?=[^\-\w]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|(?:\\[0-9a-f]+ ?))(?:[_a-z0-9\-]|\\(?:\\[0-9a-f]+ ?))*)\s*:/i],[PR.PR_COMMENT,/^\/\*[^*]*\*+(?:[^\/*][^*]*\*+)*\//],[PR.PR_COMMENT,/^(?:)/],[PR.PR_LITERAL,/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],[PR.PR_LITERAL,/^#(?:[0-9a-f]{3}){1,2}/i],[PR.PR_PLAIN,/^-?(?:[_a-z]|(?:\\[\da-f]+ ?))(?:[_a-z\d\-]|\\(?:\\[\da-f]+ ?))*/i],[PR.PR_PUNCTUATION,/^[^\s\w\'\"]+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[[PR.PR_KEYWORD,/^-?(?:[_a-z]|(?:\\[\da-f]+ ?))(?:[_a-z\d\-]|\\(?:\\[\da-f]+ ?))*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[[PR.PR_STRING,/^[^\)\"\']+/]]),["css-str"]); diff --git a/coverage/lcov-report/sort-arrow-sprite.png b/coverage/lcov-report/sort-arrow-sprite.png new file mode 100644 index 0000000..6ed6831 Binary files /dev/null and b/coverage/lcov-report/sort-arrow-sprite.png differ diff --git a/coverage/lcov-report/sorter.js b/coverage/lcov-report/sorter.js new file mode 100644 index 0000000..2bb296a --- /dev/null +++ b/coverage/lcov-report/sorter.js @@ -0,0 +1,196 @@ +/* eslint-disable */ +var addSorting = (function() { + 'use strict'; + var cols, + currentSort = { + index: 0, + desc: false + }; + + // returns the summary table element + function getTable() { + return document.querySelector('.coverage-summary'); + } + // returns the thead element of the summary table + function getTableHeader() { + return getTable().querySelector('thead tr'); + } + // returns the tbody element of the summary table + function getTableBody() { + return getTable().querySelector('tbody'); + } + // returns the th element for nth column + function getNthColumn(n) { + return getTableHeader().querySelectorAll('th')[n]; + } + + function onFilterInput() { + const searchValue = document.getElementById('fileSearch').value; + const rows = document.getElementsByTagName('tbody')[0].children; + for (let i = 0; i < rows.length; i++) { + const row = rows[i]; + if ( + row.textContent + .toLowerCase() + .includes(searchValue.toLowerCase()) + ) { + row.style.display = ''; + } else { + row.style.display = 'none'; + } + } + } + + // loads the search box + function addSearchBox() { + var template = document.getElementById('filterTemplate'); + var templateClone = template.content.cloneNode(true); + templateClone.getElementById('fileSearch').oninput = onFilterInput; + template.parentElement.appendChild(templateClone); + } + + // loads all columns + function loadColumns() { + var colNodes = getTableHeader().querySelectorAll('th'), + colNode, + cols = [], + col, + i; + + for (i = 0; i < colNodes.length; i += 1) { + colNode = colNodes[i]; + col = { + key: colNode.getAttribute('data-col'), + sortable: !colNode.getAttribute('data-nosort'), + type: colNode.getAttribute('data-type') || 'string' + }; + cols.push(col); + if (col.sortable) { + col.defaultDescSort = col.type === 'number'; + colNode.innerHTML = + colNode.innerHTML + ''; + } + } + return cols; + } + // attaches a data attribute to every tr element with an object + // of data values keyed by column name + function loadRowData(tableRow) { + var tableCols = tableRow.querySelectorAll('td'), + colNode, + col, + data = {}, + i, + val; + for (i = 0; i < tableCols.length; i += 1) { + colNode = tableCols[i]; + col = cols[i]; + val = colNode.getAttribute('data-value'); + if (col.type === 'number') { + val = Number(val); + } + data[col.key] = val; + } + return data; + } + // loads all row data + function loadData() { + var rows = getTableBody().querySelectorAll('tr'), + i; + + for (i = 0; i < rows.length; i += 1) { + rows[i].data = loadRowData(rows[i]); + } + } + // sorts the table using the data for the ith column + function sortByIndex(index, desc) { + var key = cols[index].key, + sorter = function(a, b) { + a = a.data[key]; + b = b.data[key]; + return a < b ? -1 : a > b ? 1 : 0; + }, + finalSorter = sorter, + tableBody = document.querySelector('.coverage-summary tbody'), + rowNodes = tableBody.querySelectorAll('tr'), + rows = [], + i; + + if (desc) { + finalSorter = function(a, b) { + return -1 * sorter(a, b); + }; + } + + for (i = 0; i < rowNodes.length; i += 1) { + rows.push(rowNodes[i]); + tableBody.removeChild(rowNodes[i]); + } + + rows.sort(finalSorter); + + for (i = 0; i < rows.length; i += 1) { + tableBody.appendChild(rows[i]); + } + } + // removes sort indicators for current column being sorted + function removeSortIndicators() { + var col = getNthColumn(currentSort.index), + cls = col.className; + + cls = cls.replace(/ sorted$/, '').replace(/ sorted-desc$/, ''); + col.className = cls; + } + // adds sort indicators for current column being sorted + function addSortIndicators() { + getNthColumn(currentSort.index).className += currentSort.desc + ? ' sorted-desc' + : ' sorted'; + } + // adds event listeners for all sorter widgets + function enableUI() { + var i, + el, + ithSorter = function ithSorter(i) { + var col = cols[i]; + + return function() { + var desc = col.defaultDescSort; + + if (currentSort.index === i) { + desc = !currentSort.desc; + } + sortByIndex(i, desc); + removeSortIndicators(); + currentSort.index = i; + currentSort.desc = desc; + addSortIndicators(); + }; + }; + for (i = 0; i < cols.length; i += 1) { + if (cols[i].sortable) { + // add the click event handler on the th so users + // dont have to click on those tiny arrows + el = getNthColumn(i).querySelector('.sorter').parentElement; + if (el.addEventListener) { + el.addEventListener('click', ithSorter(i)); + } else { + el.attachEvent('onclick', ithSorter(i)); + } + } + } + } + // adds sorting functionality to the UI + return function() { + if (!getTable()) { + return; + } + cols = loadColumns(); + loadData(); + addSearchBox(); + addSortIndicators(); + enableUI(); + }; +})(); + +window.addEventListener('load', addSorting); diff --git a/coverage/lcov.info b/coverage/lcov.info new file mode 100644 index 0000000..f66f267 --- /dev/null +++ b/coverage/lcov.info @@ -0,0 +1,3998 @@ +TN: +SF:app.ts +FNF:0 +FNH:0 +DA:2,1 +DA:3,1 +DA:4,1 +DA:5,1 +DA:6,1 +DA:7,1 +DA:8,1 +DA:9,1 +DA:10,1 +DA:11,1 +DA:12,1 +DA:13,1 +DA:14,1 +DA:17,1 +DA:18,1 +DA:20,1 +DA:22,1 +DA:24,1 +DA:27,1 +DA:28,1 +DA:29,1 +DA:30,1 +DA:31,1 +DA:32,1 +DA:33,1 +DA:34,1 +DA:35,1 +DA:36,1 +DA:37,1 +DA:38,1 +DA:39,1 +DA:40,1 +DA:41,1 +DA:42,1 +DA:43,1 +DA:44,1 +DA:45,1 +DA:46,1 +DA:47,1 +DA:48,1 +DA:49,1 +DA:50,1 +DA:51,1 +DA:52,1 +DA:54,1 +DA:55,1 +DA:56,1 +DA:57,1 +DA:58,1 +DA:59,1 +DA:60,1 +DA:61,1 +DA:62,1 +DA:63,1 +DA:64,1 +DA:65,1 +DA:66,1 +DA:67,1 +DA:68,1 +DA:69,1 +DA:70,1 +DA:71,1 +DA:72,1 +DA:73,1 +DA:74,1 +DA:75,1 +DA:76,1 +DA:77,1 +DA:79,1 +DA:80,1 +DA:81,1 +DA:82,1 +DA:83,1 +DA:84,1 +DA:85,1 +DA:86,1 +DA:87,1 +DA:88,1 +DA:91,1 +DA:94,1 +DA:97,1 +DA:98,0 +DA:99,0 +DA:102,1 +DA:103,1 +DA:104,1 +DA:106,1 +DA:107,1 +DA:108,1 +DA:111,1 +DA:114,1 +DA:135,1 +DA:136,0 +DA:137,0 +DA:140,1 +DA:141,1 +DA:142,1 +DA:145,1 +DA:148,1 +DA:149,0 +DA:150,0 +DA:151,0 +DA:152,0 +DA:153,0 +DA:154,0 +DA:155,0 +DA:156,0 +DA:157,0 +DA:159,1 +LF:109 +LH:96 +BRDA:87,0,0,0 +BRDA:97,1,0,0 +BRDA:148,2,0,0 +BRDA:140,3,0,1 +BRF:4 +BRH:1 +end_of_record +TN: +SF:src/config/config.ts +FN:1,(empty-report) +FNF:1 +FNH:0 +FNDA:0,(empty-report) +DA:1,0 +DA:3,0 +DA:5,0 +DA:6,0 +DA:7,0 +DA:8,0 +DA:9,0 +DA:10,0 +DA:11,0 +DA:12,0 +DA:13,0 +DA:14,0 +DA:15,0 +DA:16,0 +DA:17,0 +DA:18,0 +DA:19,0 +DA:20,0 +DA:21,0 +DA:22,0 +DA:23,0 +DA:24,0 +DA:25,0 +DA:26,0 +DA:27,0 +LF:25 +LH:0 +BRDA:1,0,0,0 +BRF:1 +BRH:0 +end_of_record +TN: +SF:src/controllers/auth.controller.ts +FN:76,generateOTP +FN:79,sendOTP +FN:132,verifyPhone +FN:245,register_user +FN:306,verifyOtp +FN:363,logIn +FN:431,refreshToken +FN:482,authHandler +FN:632,sendNewOTP +FNF:9 +FNH:2 +FNDA:0,generateOTP +FNDA:0,sendOTP +FNDA:2,verifyPhone +FNDA:0,register_user +FNDA:0,verifyOtp +FNDA:1,logIn +FNDA:0,refreshToken +FNDA:0,authHandler +FNDA:0,sendNewOTP +DA:1,1 +DA:2,1 +DA:3,1 +DA:4,1 +DA:5,1 +DA:6,1 +DA:7,1 +DA:8,1 +DA:9,1 +DA:10,1 +DA:11,1 +DA:13,1 +DA:14,1 +DA:67,1 +DA:68,1 +DA:69,1 +DA:70,1 +DA:71,1 +DA:73,1 +DA:76,1 +DA:79,1 +DA:80,0 +DA:81,0 +DA:82,0 +DA:83,0 +DA:84,0 +DA:85,0 +DA:86,0 +DA:87,0 +DA:88,0 +DA:89,0 +DA:90,0 +DA:92,0 +DA:93,0 +DA:94,0 +DA:95,0 +DA:96,0 +DA:98,0 +DA:99,0 +DA:104,0 +DA:106,0 +DA:107,0 +DA:109,0 +DA:110,0 +DA:111,0 +DA:112,0 +DA:113,0 +DA:114,0 +DA:115,0 +DA:116,0 +DA:118,0 +DA:119,0 +DA:120,0 +DA:121,0 +DA:122,0 +DA:123,0 +DA:124,0 +DA:125,0 +DA:127,0 +DA:128,0 +DA:129,0 +DA:132,1 +DA:133,2 +DA:134,2 +DA:135,2 +DA:136,2 +DA:137,2 +DA:138,2 +DA:141,2 +DA:142,1 +DA:143,1 +DA:145,2 +DA:146,0 +DA:147,0 +DA:149,2 +DA:150,0 +DA:151,0 +DA:153,1 +DA:154,1 +DA:155,1 +DA:156,1 +DA:157,2 +DA:158,0 +DA:159,0 +DA:160,0 +DA:161,0 +DA:162,0 +DA:165,1 +DA:166,0 +DA:167,0 +DA:168,0 +DA:171,0 +DA:172,0 +DA:173,0 +DA:174,0 +DA:177,0 +DA:180,0 +DA:181,0 +DA:182,0 +DA:183,0 +DA:184,0 +DA:185,0 +DA:186,2 +DA:187,2 +DA:188,2 +DA:189,2 +DA:190,2 +DA:191,2 +DA:192,2 +DA:193,2 +DA:194,2 +DA:195,2 +DA:196,2 +DA:197,2 +DA:199,0 +DA:200,0 +DA:201,0 +DA:204,0 +DA:205,0 +DA:208,0 +DA:209,0 +DA:210,0 +DA:211,0 +DA:212,0 +DA:213,0 +DA:214,0 +DA:216,0 +DA:217,0 +DA:218,0 +DA:221,0 +DA:224,0 +DA:225,0 +DA:226,0 +DA:227,0 +DA:228,0 +DA:229,0 +DA:230,2 +DA:231,2 +DA:232,2 +DA:233,2 +DA:234,2 +DA:235,2 +DA:236,2 +DA:237,2 +DA:238,2 +DA:239,0 +DA:240,0 +DA:241,2 +DA:242,2 +DA:245,1 +DA:246,0 +DA:247,0 +DA:248,0 +DA:249,0 +DA:250,0 +DA:251,0 +DA:252,0 +DA:253,0 +DA:254,0 +DA:255,0 +DA:256,0 +DA:257,0 +DA:258,0 +DA:259,0 +DA:260,0 +DA:261,0 +DA:262,0 +DA:264,0 +DA:265,0 +DA:266,0 +DA:267,0 +DA:269,0 +DA:270,0 +DA:271,0 +DA:272,0 +DA:273,0 +DA:274,0 +DA:275,0 +DA:276,0 +DA:278,0 +DA:279,0 +DA:280,0 +DA:281,0 +DA:282,0 +DA:283,0 +DA:284,0 +DA:286,0 +DA:287,0 +DA:288,0 +DA:289,0 +DA:290,0 +DA:291,0 +DA:292,0 +DA:293,0 +DA:294,0 +DA:295,0 +DA:296,0 +DA:297,0 +DA:299,0 +DA:300,0 +DA:301,0 +DA:302,0 +DA:303,0 +DA:306,1 +DA:307,0 +DA:308,0 +DA:309,0 +DA:310,0 +DA:312,0 +DA:313,0 +DA:314,0 +DA:315,0 +DA:316,0 +DA:317,0 +DA:318,0 +DA:320,0 +DA:321,0 +DA:322,0 +DA:323,0 +DA:324,0 +DA:325,0 +DA:326,0 +DA:327,0 +DA:329,0 +DA:330,0 +DA:331,0 +DA:332,0 +DA:333,0 +DA:334,0 +DA:335,0 +DA:337,0 +DA:338,0 +DA:339,0 +DA:340,0 +DA:342,0 +DA:343,0 +DA:344,0 +DA:345,0 +DA:346,0 +DA:347,0 +DA:348,0 +DA:349,0 +DA:350,0 +DA:351,0 +DA:352,0 +DA:353,0 +DA:354,0 +DA:355,0 +DA:356,0 +DA:357,0 +DA:358,0 +DA:359,0 +DA:360,0 +DA:363,1 +DA:364,1 +DA:365,1 +DA:366,1 +DA:367,1 +DA:368,1 +DA:369,1 +DA:371,1 +DA:372,1 +DA:373,1 +DA:374,1 +DA:375,1 +DA:376,1 +DA:377,1 +DA:378,1 +DA:379,1 +DA:381,0 +DA:382,0 +DA:383,0 +DA:385,0 +DA:386,0 +DA:387,0 +DA:388,0 +DA:390,0 +DA:391,0 +DA:392,0 +DA:393,0 +DA:394,0 +DA:395,0 +DA:396,0 +DA:397,0 +DA:399,0 +DA:400,0 +DA:401,0 +DA:402,0 +DA:403,0 +DA:404,0 +DA:405,0 +DA:406,0 +DA:408,0 +DA:410,0 +DA:411,0 +DA:412,0 +DA:413,0 +DA:414,0 +DA:415,0 +DA:416,1 +DA:417,1 +DA:418,1 +DA:419,1 +DA:420,1 +DA:421,1 +DA:422,1 +DA:423,1 +DA:424,1 +DA:425,0 +DA:426,0 +DA:427,1 +DA:428,1 +DA:431,1 +DA:432,0 +DA:433,0 +DA:434,0 +DA:435,0 +DA:436,0 +DA:437,0 +DA:439,0 +DA:440,0 +DA:441,0 +DA:442,0 +DA:443,0 +DA:446,0 +DA:449,0 +DA:450,0 +DA:451,0 +DA:452,0 +DA:454,0 +DA:455,0 +DA:456,0 +DA:457,0 +DA:458,0 +DA:459,0 +DA:462,0 +DA:463,0 +DA:464,0 +DA:465,0 +DA:466,0 +DA:468,0 +DA:469,0 +DA:470,0 +DA:471,0 +DA:472,0 +DA:473,0 +DA:474,0 +DA:475,0 +DA:476,0 +DA:477,0 +DA:478,0 +DA:479,0 +DA:482,1 +DA:483,0 +DA:484,0 +DA:485,0 +DA:486,0 +DA:487,0 +DA:489,0 +DA:490,0 +DA:491,0 +DA:492,0 +DA:495,0 +DA:496,0 +DA:497,0 +DA:498,0 +DA:500,0 +DA:503,0 +DA:504,0 +DA:505,0 +DA:506,0 +DA:507,0 +DA:509,0 +DA:510,0 +DA:511,0 +DA:512,0 +DA:514,0 +DA:515,0 +DA:516,0 +DA:517,0 +DA:518,0 +DA:519,0 +DA:520,0 +DA:522,0 +DA:524,0 +DA:525,0 +DA:526,0 +DA:527,0 +DA:528,0 +DA:529,0 +DA:530,0 +DA:531,0 +DA:532,0 +DA:533,0 +DA:534,0 +DA:535,0 +DA:536,0 +DA:537,0 +DA:538,0 +DA:539,0 +DA:540,0 +DA:541,0 +DA:542,0 +DA:543,0 +DA:544,0 +DA:545,0 +DA:546,0 +DA:547,0 +DA:548,0 +DA:549,0 +DA:550,0 +DA:551,0 +DA:552,0 +DA:555,0 +DA:556,0 +DA:562,0 +DA:563,0 +DA:565,0 +DA:567,0 +DA:568,0 +DA:569,0 +DA:570,0 +DA:571,0 +DA:573,0 +DA:574,0 +DA:576,0 +DA:578,0 +DA:579,0 +DA:580,0 +DA:581,0 +DA:582,0 +DA:583,0 +DA:584,0 +DA:585,0 +DA:586,0 +DA:587,0 +DA:588,0 +DA:589,0 +DA:590,0 +DA:591,0 +DA:592,0 +DA:593,0 +DA:594,0 +DA:595,0 +DA:596,0 +DA:597,0 +DA:598,0 +DA:599,0 +DA:600,0 +DA:601,0 +DA:602,0 +DA:603,0 +DA:604,0 +DA:605,0 +DA:606,0 +DA:608,0 +DA:609,0 +DA:610,0 +DA:611,0 +DA:612,0 +DA:615,0 +DA:616,0 +DA:617,0 +DA:618,0 +DA:619,0 +DA:621,0 +DA:622,0 +DA:623,0 +DA:624,0 +DA:625,0 +DA:626,0 +DA:627,0 +DA:628,0 +DA:629,0 +DA:632,1 +DA:633,0 +DA:634,0 +DA:635,0 +DA:636,0 +DA:637,0 +DA:638,0 +DA:639,0 +DA:640,0 +DA:642,0 +DA:643,0 +DA:644,0 +DA:645,0 +DA:646,0 +DA:647,0 +DA:648,0 +DA:649,0 +DA:651,0 +DA:654,0 +DA:657,0 +DA:658,0 +DA:659,0 +DA:660,0 +DA:661,0 +DA:662,0 +DA:663,0 +DA:664,0 +DA:666,0 +DA:667,0 +DA:668,0 +DA:671,0 +DA:672,0 +DA:673,0 +DA:674,0 +DA:675,0 +DA:676,0 +DA:677,0 +DA:678,0 +DA:679,0 +DA:680,0 +DA:681,0 +DA:682,0 +LF:516 +LH:94 +BRDA:132,0,0,2 +BRDA:141,1,0,1 +BRDA:141,2,0,1 +BRDA:145,3,0,0 +BRDA:147,4,0,1 +BRDA:149,5,0,0 +BRDA:151,6,0,1 +BRDA:157,7,0,0 +BRDA:162,8,0,1 +BRDA:165,9,0,0 +BRDA:197,10,0,0 +BRDA:235,11,0,1 +BRDA:235,12,0,1 +BRDA:238,13,0,0 +BRDA:363,14,0,1 +BRDA:379,15,0,0 +BRDA:421,16,0,0 +BRDA:424,17,0,0 +BRF:18 +BRH:9 +end_of_record +TN: +SF:src/controllers/multerStorage.ts +FN:1,(empty-report) +FNF:1 +FNH:0 +FNDA:0,(empty-report) +DA:1,0 +DA:2,0 +DA:4,0 +DA:11,0 +DA:12,0 +DA:13,0 +DA:14,0 +DA:15,0 +DA:16,0 +DA:17,0 +DA:18,0 +DA:19,0 +DA:20,0 +DA:21,0 +DA:22,0 +DA:25,0 +DA:26,0 +DA:27,0 +DA:28,0 +DA:29,0 +DA:30,0 +DA:32,0 +DA:33,0 +DA:34,0 +DA:35,0 +DA:36,0 +DA:37,0 +DA:38,0 +DA:41,0 +DA:42,0 +DA:44,0 +DA:45,0 +DA:46,0 +DA:47,0 +DA:48,0 +DA:50,0 +DA:51,0 +DA:52,0 +DA:57,0 +DA:58,0 +DA:59,0 +DA:60,0 +DA:61,0 +DA:62,0 +DA:63,0 +DA:64,0 +DA:65,0 +DA:66,0 +DA:67,0 +DA:68,0 +DA:69,0 +DA:70,0 +DA:71,0 +DA:72,0 +DA:73,0 +DA:75,0 +LF:56 +LH:0 +BRDA:1,0,0,0 +BRF:1 +BRH:0 +end_of_record +TN: +SF:src/controllers/notification.controller.ts +FN:29,getNotification +FN:55,createNotification +FN:94,markAsRead +FN:124,testExpoNotification +FNF:4 +FNH:0 +FNDA:0,getNotification +FNDA:0,createNotification +FNDA:0,markAsRead +FNDA:0,testExpoNotification +DA:1,1 +DA:2,1 +DA:4,1 +DA:29,1 +DA:30,0 +DA:31,0 +DA:32,0 +DA:33,0 +DA:34,0 +DA:36,0 +DA:37,0 +DA:38,0 +DA:39,0 +DA:40,0 +DA:41,0 +DA:42,0 +DA:43,0 +DA:44,0 +DA:46,0 +DA:47,0 +DA:48,0 +DA:49,0 +DA:50,0 +DA:51,0 +DA:52,0 +DA:55,1 +DA:56,0 +DA:57,0 +DA:58,0 +DA:59,0 +DA:60,0 +DA:61,0 +DA:63,0 +DA:64,0 +DA:65,0 +DA:66,0 +DA:68,0 +DA:69,0 +DA:70,0 +DA:71,0 +DA:72,0 +DA:73,0 +DA:74,0 +DA:75,0 +DA:77,0 +DA:78,0 +DA:79,0 +DA:81,0 +DA:82,0 +DA:83,0 +DA:84,0 +DA:85,0 +DA:86,0 +DA:87,0 +DA:88,0 +DA:89,0 +DA:90,0 +DA:91,0 +DA:94,1 +DA:95,0 +DA:96,0 +DA:97,0 +DA:98,0 +DA:99,0 +DA:101,0 +DA:102,0 +DA:103,0 +DA:104,0 +DA:105,0 +DA:107,0 +DA:108,0 +DA:109,0 +DA:111,0 +DA:112,0 +DA:113,0 +DA:114,0 +DA:115,0 +DA:116,0 +DA:117,0 +DA:118,0 +DA:119,0 +DA:120,0 +DA:121,0 +DA:124,1 +DA:125,0 +DA:126,0 +DA:127,0 +DA:128,0 +DA:129,0 +DA:131,0 +DA:132,0 +DA:133,0 +DA:134,0 +DA:135,0 +DA:137,0 +DA:138,0 +DA:139,0 +DA:141,0 +DA:142,0 +DA:143,0 +DA:144,0 +DA:145,0 +DA:146,0 +DA:147,0 +DA:149,0 +DA:150,0 +DA:151,0 +DA:152,0 +DA:153,0 +DA:154,0 +DA:155,0 +DA:156,0 +DA:158,0 +DA:159,0 +DA:160,0 +DA:161,0 +DA:162,0 +DA:163,0 +DA:165,0 +DA:168,0 +DA:169,0 +DA:170,0 +DA:171,0 +DA:173,0 +DA:174,0 +DA:175,0 +DA:176,0 +DA:177,0 +DA:178,0 +DA:179,0 +DA:180,0 +DA:181,0 +DA:182,0 +DA:183,0 +DA:184,0 +LF:135 +LH:7 +BRF:0 +BRH:0 +end_of_record +TN: +SF:src/controllers/order.controller.ts +FN:68,getOrderById +FN:141,getBuyerOrders +FN:206,getSellerOrders +FN:284,createOrder +FN:351,updateOrder +FN:455,getTransaction +FN:484,updateDispatchDetails +FNF:7 +FNH:0 +FNDA:0,getOrderById +FNDA:0,getBuyerOrders +FNDA:0,getSellerOrders +FNDA:0,createOrder +FNDA:0,updateOrder +FNDA:0,getTransaction +FNDA:0,updateDispatchDetails +DA:1,1 +DA:2,1 +DA:3,1 +DA:4,1 +DA:5,1 +DA:6,1 +DA:7,1 +DA:8,1 +DA:9,1 +DA:10,1 +DA:11,1 +DA:15,1 +DA:61,1 +DA:62,1 +DA:63,1 +DA:64,1 +DA:65,1 +DA:68,1 +DA:69,0 +DA:70,0 +DA:71,0 +DA:72,0 +DA:73,0 +DA:74,0 +DA:75,0 +DA:76,0 +DA:77,0 +DA:78,0 +DA:79,0 +DA:80,0 +DA:81,0 +DA:82,0 +DA:83,0 +DA:84,0 +DA:85,0 +DA:86,0 +DA:87,0 +DA:88,0 +DA:89,0 +DA:90,0 +DA:91,0 +DA:92,0 +DA:93,0 +DA:94,0 +DA:95,0 +DA:96,0 +DA:97,0 +DA:98,0 +DA:99,0 +DA:100,0 +DA:101,0 +DA:102,0 +DA:103,0 +DA:104,0 +DA:105,0 +DA:106,0 +DA:107,0 +DA:108,0 +DA:109,0 +DA:110,0 +DA:111,0 +DA:112,0 +DA:113,0 +DA:114,0 +DA:115,0 +DA:116,0 +DA:117,0 +DA:118,0 +DA:119,0 +DA:121,0 +DA:122,0 +DA:123,0 +DA:125,0 +DA:126,0 +DA:127,0 +DA:128,0 +DA:129,0 +DA:130,0 +DA:131,0 +DA:132,0 +DA:133,0 +DA:134,0 +DA:135,0 +DA:136,0 +DA:137,0 +DA:138,0 +DA:141,1 +DA:142,0 +DA:143,0 +DA:144,0 +DA:145,0 +DA:146,0 +DA:148,0 +DA:149,0 +DA:150,0 +DA:152,0 +DA:153,0 +DA:154,0 +DA:156,0 +DA:157,0 +DA:158,0 +DA:159,0 +DA:160,0 +DA:161,0 +DA:162,0 +DA:163,0 +DA:164,0 +DA:165,0 +DA:166,0 +DA:167,0 +DA:168,0 +DA:169,0 +DA:170,0 +DA:171,0 +DA:172,0 +DA:173,0 +DA:174,0 +DA:175,0 +DA:176,0 +DA:177,0 +DA:178,0 +DA:179,0 +DA:180,0 +DA:181,0 +DA:182,0 +DA:183,0 +DA:184,0 +DA:185,0 +DA:186,0 +DA:187,0 +DA:188,0 +DA:189,0 +DA:190,0 +DA:191,0 +DA:192,0 +DA:194,0 +DA:195,0 +DA:196,0 +DA:197,0 +DA:198,0 +DA:199,0 +DA:200,0 +DA:201,0 +DA:202,0 +DA:203,0 +DA:206,1 +DA:207,0 +DA:213,0 +DA:214,0 +DA:215,0 +DA:216,0 +DA:218,0 +DA:219,0 +DA:220,0 +DA:222,0 +DA:224,0 +DA:225,0 +DA:226,0 +DA:227,0 +DA:228,0 +DA:229,0 +DA:230,0 +DA:231,0 +DA:233,0 +DA:234,0 +DA:235,0 +DA:236,0 +DA:237,0 +DA:238,0 +DA:239,0 +DA:240,0 +DA:241,0 +DA:242,0 +DA:243,0 +DA:244,0 +DA:245,0 +DA:246,0 +DA:247,0 +DA:248,0 +DA:249,0 +DA:250,0 +DA:251,0 +DA:252,0 +DA:253,0 +DA:254,0 +DA:255,0 +DA:256,0 +DA:257,0 +DA:258,0 +DA:259,0 +DA:260,0 +DA:261,0 +DA:262,0 +DA:263,0 +DA:264,0 +DA:265,0 +DA:266,0 +DA:267,0 +DA:268,0 +DA:269,0 +DA:270,0 +DA:272,0 +DA:273,0 +DA:274,0 +DA:275,0 +DA:276,0 +DA:277,0 +DA:278,0 +DA:279,0 +DA:280,0 +DA:281,0 +DA:284,1 +DA:285,0 +DA:286,0 +DA:287,0 +DA:288,0 +DA:290,0 +DA:291,0 +DA:294,0 +DA:295,0 +DA:296,0 +DA:299,0 +DA:300,0 +DA:301,0 +DA:302,0 +DA:304,0 +DA:305,0 +DA:306,0 +DA:307,0 +DA:308,0 +DA:309,0 +DA:310,0 +DA:311,0 +DA:312,0 +DA:313,0 +DA:314,0 +DA:315,0 +DA:316,0 +DA:317,0 +DA:318,0 +DA:320,0 +DA:321,0 +DA:322,0 +DA:323,0 +DA:324,0 +DA:325,0 +DA:326,0 +DA:327,0 +DA:328,0 +DA:329,0 +DA:331,0 +DA:333,0 +DA:334,0 +DA:335,0 +DA:336,0 +DA:337,0 +DA:338,0 +DA:339,0 +DA:340,0 +DA:341,0 +DA:342,0 +DA:343,0 +DA:344,0 +DA:345,0 +DA:346,0 +DA:347,0 +DA:348,0 +DA:351,1 +DA:352,0 +DA:353,0 +DA:354,0 +DA:355,0 +DA:356,0 +DA:357,0 +DA:358,0 +DA:360,0 +DA:361,0 +DA:362,0 +DA:364,0 +DA:365,0 +DA:366,0 +DA:367,0 +DA:368,0 +DA:369,0 +DA:371,0 +DA:372,0 +DA:373,0 +DA:374,0 +DA:375,0 +DA:376,0 +DA:377,0 +DA:378,0 +DA:379,0 +DA:381,0 +DA:382,0 +DA:383,0 +DA:384,0 +DA:386,0 +DA:387,0 +DA:388,0 +DA:389,0 +DA:392,0 +DA:393,0 +DA:394,0 +DA:395,0 +DA:396,0 +DA:398,0 +DA:399,0 +DA:400,0 +DA:401,0 +DA:403,0 +DA:404,0 +DA:405,0 +DA:406,0 +DA:408,0 +DA:409,0 +DA:410,0 +DA:411,0 +DA:412,0 +DA:413,0 +DA:414,0 +DA:415,0 +DA:417,0 +DA:418,0 +DA:419,0 +DA:420,0 +DA:421,0 +DA:423,0 +DA:424,0 +DA:425,0 +DA:426,0 +DA:428,0 +DA:429,0 +DA:430,0 +DA:431,0 +DA:433,0 +DA:434,0 +DA:435,0 +DA:436,0 +DA:437,0 +DA:438,0 +DA:439,0 +DA:440,0 +DA:442,0 +DA:443,0 +DA:444,0 +DA:445,0 +DA:446,0 +DA:447,0 +DA:448,0 +DA:449,0 +DA:450,0 +DA:451,0 +DA:452,0 +DA:455,1 +DA:456,0 +DA:457,0 +DA:458,0 +DA:459,0 +DA:460,0 +DA:461,0 +DA:462,0 +DA:464,0 +DA:465,0 +DA:466,0 +DA:468,0 +DA:469,0 +DA:470,0 +DA:471,0 +DA:472,0 +DA:473,0 +DA:474,0 +DA:475,0 +DA:476,0 +DA:477,0 +DA:478,0 +DA:479,0 +DA:480,0 +DA:481,0 +DA:484,1 +DA:485,0 +DA:486,0 +DA:487,0 +DA:488,0 +DA:489,0 +DA:490,0 +DA:491,0 +DA:493,0 +DA:494,0 +DA:495,0 +DA:497,0 +DA:498,0 +DA:499,0 +DA:500,0 +DA:502,0 +DA:503,0 +DA:504,0 +DA:505,0 +DA:506,0 +DA:507,0 +DA:508,0 +DA:510,0 +DA:511,0 +DA:512,0 +DA:514,0 +DA:515,0 +DA:516,0 +DA:517,0 +DA:518,0 +DA:519,0 +DA:520,0 +DA:521,0 +DA:522,0 +DA:523,0 +DA:524,0 +DA:525,0 +DA:527,0 +DA:528,0 +DA:529,0 +DA:531,0 +DA:532,0 +DA:533,0 +DA:534,0 +DA:535,0 +DA:536,0 +DA:537,0 +DA:538,0 +DA:539,0 +DA:540,0 +DA:541,0 +LF:430 +LH:24 +BRF:0 +BRH:0 +end_of_record +TN: +SF:src/controllers/payment.collection.controller.ts +FN:1,(empty-report) +FNF:1 +FNH:0 +FNDA:0,(empty-report) +DA:1,0 +DA:2,0 +DA:3,0 +DA:4,0 +DA:6,0 +DA:7,0 +DA:8,0 +DA:11,0 +DA:48,0 +DA:49,0 +DA:50,0 +DA:51,0 +DA:53,0 +DA:54,0 +DA:55,0 +DA:58,0 +DA:59,0 +DA:60,0 +DA:61,0 +DA:62,0 +DA:64,0 +DA:65,0 +DA:66,0 +DA:67,0 +DA:68,0 +DA:69,0 +DA:70,0 +DA:71,0 +DA:72,0 +DA:74,0 +DA:75,0 +DA:76,0 +DA:77,0 +DA:78,0 +DA:79,0 +DA:80,0 +DA:81,0 +DA:82,0 +DA:83,0 +DA:84,0 +DA:85,0 +DA:88,0 +DA:89,0 +DA:90,0 +DA:91,0 +DA:92,0 +DA:93,0 +DA:94,0 +DA:95,0 +DA:96,0 +DA:97,0 +DA:98,0 +DA:99,0 +DA:100,0 +DA:101,0 +DA:102,0 +DA:104,0 +DA:105,0 +DA:106,0 +DA:107,0 +DA:108,0 +DA:109,0 +DA:110,0 +DA:111,0 +DA:112,0 +DA:113,0 +DA:114,0 +DA:115,0 +DA:118,0 +DA:119,0 +DA:120,0 +DA:121,0 +DA:122,0 +DA:123,0 +DA:124,0 +DA:125,0 +DA:126,0 +DA:127,0 +DA:129,0 +DA:130,0 +DA:131,0 +DA:132,0 +DA:133,0 +DA:134,0 +DA:135,0 +DA:136,0 +DA:137,0 +DA:138,0 +DA:140,0 +DA:141,0 +DA:142,0 +DA:143,0 +DA:144,0 +DA:145,0 +DA:146,0 +DA:147,0 +DA:148,0 +DA:149,0 +DA:150,0 +DA:151,0 +DA:154,0 +DA:155,0 +DA:156,0 +DA:157,0 +DA:158,0 +DA:159,0 +DA:160,0 +DA:162,0 +DA:163,0 +DA:164,0 +DA:165,0 +DA:166,0 +DA:168,0 +DA:169,0 +DA:170,0 +DA:171,0 +DA:172,0 +DA:173,0 +DA:174,0 +DA:177,0 +DA:178,0 +DA:179,0 +DA:180,0 +DA:181,0 +DA:183,0 +DA:184,0 +DA:185,0 +DA:186,0 +DA:187,0 +DA:188,0 +DA:189,0 +DA:191,0 +DA:192,0 +DA:193,0 +DA:194,0 +DA:195,0 +DA:196,0 +DA:197,0 +DA:199,0 +DA:200,0 +DA:201,0 +DA:202,0 +DA:203,0 +DA:204,0 +DA:205,0 +DA:206,0 +DA:207,0 +DA:208,0 +DA:209,0 +DA:210,0 +DA:211,0 +DA:213,0 +DA:215,0 +DA:216,0 +DA:217,0 +DA:218,0 +DA:219,0 +DA:220,0 +DA:221,0 +DA:222,0 +DA:223,0 +DA:224,0 +DA:225,0 +DA:226,0 +DA:227,0 +DA:228,0 +DA:229,0 +DA:230,0 +DA:231,0 +DA:232,0 +DA:233,0 +DA:234,0 +DA:235,0 +DA:236,0 +DA:237,0 +DA:238,0 +DA:239,0 +DA:240,0 +DA:241,0 +DA:242,0 +DA:243,0 +DA:244,0 +DA:245,0 +DA:246,0 +DA:247,0 +DA:248,0 +DA:249,0 +DA:250,0 +DA:251,0 +DA:252,0 +DA:253,0 +DA:254,0 +DA:257,0 +DA:258,0 +DA:259,0 +DA:260,0 +DA:261,0 +DA:262,0 +DA:264,0 +DA:265,0 +DA:266,0 +DA:267,0 +DA:269,0 +DA:270,0 +DA:271,0 +DA:272,0 +DA:274,0 +DA:275,0 +DA:276,0 +DA:277,0 +DA:278,0 +DA:280,0 +DA:281,0 +DA:282,0 +DA:284,0 +DA:285,0 +DA:286,0 +DA:287,0 +DA:289,0 +DA:290,0 +DA:291,0 +DA:292,0 +DA:294,0 +DA:295,0 +DA:296,0 +DA:297,0 +DA:298,0 +DA:299,0 +DA:300,0 +DA:301,0 +DA:302,0 +DA:303,0 +DA:304,0 +DA:305,0 +DA:306,0 +DA:307,0 +DA:308,0 +DA:309,0 +DA:310,0 +DA:311,0 +DA:312,0 +DA:315,0 +DA:316,0 +DA:317,0 +DA:318,0 +DA:319,0 +DA:321,0 +DA:322,0 +DA:323,0 +DA:324,0 +DA:326,0 +DA:327,0 +DA:328,0 +DA:329,0 +DA:331,0 +DA:332,0 +DA:333,0 +DA:334,0 +DA:335,0 +DA:336,0 +DA:337,0 +DA:338,0 +DA:340,0 +DA:341,0 +DA:342,0 +DA:343,0 +DA:344,0 +DA:345,0 +DA:347,0 +DA:348,0 +DA:349,0 +DA:350,0 +DA:352,0 +DA:353,0 +DA:354,0 +DA:355,0 +DA:357,0 +DA:358,0 +DA:359,0 +DA:360,0 +DA:361,0 +DA:362,0 +DA:363,0 +DA:364,0 +DA:366,0 +DA:367,0 +DA:368,0 +DA:369,0 +DA:370,0 +DA:371,0 +DA:372,0 +DA:373,0 +DA:374,0 +DA:375,0 +DA:376,0 +DA:377,0 +DA:378,0 +DA:379,0 +DA:380,0 +DA:381,0 +LF:300 +LH:0 +BRDA:1,0,0,0 +BRF:1 +BRH:0 +end_of_record +TN: +SF:src/controllers/product.controller.ts +FN:34,allProducts +FN:73,getProduct +FN:130,userProducts +FN:157,createProduct +FN:209,updateProduct +FN:257,removeProduct +FNF:6 +FNH:0 +FNDA:0,allProducts +FNDA:0,getProduct +FNDA:0,userProducts +FNDA:0,createProduct +FNDA:0,updateProduct +FNDA:0,removeProduct +DA:1,1 +DA:2,1 +DA:3,1 +DA:4,1 +DA:5,1 +DA:6,1 +DA:9,1 +DA:27,1 +DA:28,1 +DA:29,1 +DA:30,1 +DA:31,1 +DA:34,1 +DA:35,0 +DA:36,0 +DA:37,0 +DA:38,0 +DA:39,0 +DA:40,0 +DA:41,0 +DA:42,0 +DA:43,0 +DA:44,0 +DA:45,0 +DA:46,0 +DA:47,0 +DA:48,0 +DA:49,0 +DA:50,0 +DA:51,0 +DA:52,0 +DA:53,0 +DA:55,0 +DA:56,0 +DA:57,0 +DA:59,0 +DA:60,0 +DA:61,0 +DA:62,0 +DA:63,0 +DA:64,0 +DA:65,0 +DA:66,0 +DA:67,0 +DA:68,0 +DA:69,0 +DA:70,0 +DA:73,1 +DA:74,0 +DA:75,0 +DA:76,0 +DA:77,0 +DA:78,0 +DA:79,0 +DA:80,0 +DA:81,0 +DA:82,0 +DA:83,0 +DA:84,0 +DA:85,0 +DA:86,0 +DA:87,0 +DA:88,0 +DA:89,0 +DA:90,0 +DA:91,0 +DA:92,0 +DA:93,0 +DA:94,0 +DA:95,0 +DA:96,0 +DA:97,0 +DA:98,0 +DA:99,0 +DA:100,0 +DA:101,0 +DA:102,0 +DA:103,0 +DA:104,0 +DA:105,0 +DA:106,0 +DA:107,0 +DA:108,0 +DA:109,0 +DA:110,0 +DA:111,0 +DA:113,0 +DA:114,0 +DA:115,0 +DA:117,0 +DA:118,0 +DA:119,0 +DA:120,0 +DA:121,0 +DA:122,0 +DA:123,0 +DA:124,0 +DA:125,0 +DA:126,0 +DA:127,0 +DA:130,1 +DA:131,0 +DA:132,0 +DA:133,0 +DA:134,0 +DA:135,0 +DA:136,0 +DA:137,0 +DA:139,0 +DA:140,0 +DA:141,0 +DA:143,0 +DA:144,0 +DA:145,0 +DA:146,0 +DA:147,0 +DA:148,0 +DA:149,0 +DA:150,0 +DA:151,0 +DA:152,0 +DA:153,0 +DA:154,0 +DA:157,1 +DA:158,0 +DA:159,0 +DA:160,0 +DA:161,0 +DA:162,0 +DA:163,0 +DA:164,0 +DA:165,0 +DA:167,0 +DA:170,0 +DA:171,0 +DA:172,0 +DA:174,0 +DA:175,0 +DA:176,0 +DA:177,0 +DA:178,0 +DA:179,0 +DA:181,0 +DA:182,0 +DA:183,0 +DA:184,0 +DA:185,0 +DA:187,0 +DA:190,0 +DA:191,0 +DA:193,0 +DA:195,0 +DA:196,0 +DA:197,0 +DA:198,0 +DA:199,0 +DA:200,0 +DA:201,0 +DA:202,0 +DA:203,0 +DA:204,0 +DA:205,0 +DA:206,0 +DA:209,1 +DA:210,0 +DA:211,0 +DA:212,0 +DA:213,0 +DA:214,0 +DA:215,0 +DA:216,0 +DA:217,0 +DA:219,0 +DA:220,0 +DA:221,0 +DA:222,0 +DA:224,0 +DA:227,0 +DA:228,0 +DA:230,0 +DA:231,0 +DA:232,0 +DA:233,0 +DA:235,0 +DA:236,0 +DA:237,0 +DA:239,0 +DA:240,0 +DA:241,0 +DA:243,0 +DA:244,0 +DA:245,0 +DA:246,0 +DA:247,0 +DA:248,0 +DA:249,0 +DA:250,0 +DA:251,0 +DA:252,0 +DA:253,0 +DA:254,0 +DA:257,1 +DA:258,0 +DA:259,0 +DA:260,0 +DA:261,0 +DA:262,0 +DA:263,0 +DA:264,0 +DA:265,0 +DA:267,0 +DA:268,0 +DA:269,0 +DA:271,0 +DA:272,0 +DA:273,0 +DA:275,0 +DA:276,0 +DA:277,0 +DA:278,0 +DA:279,0 +DA:280,0 +DA:281,0 +DA:282,0 +DA:283,0 +DA:284,0 +DA:285,0 +DA:286,0 +LF:228 +LH:18 +BRF:0 +BRH:0 +end_of_record +TN: +SF:src/controllers/product.search.controller.ts +FN:27,getAllProductSearch +FNF:1 +FNH:0 +FNDA:0,getAllProductSearch +DA:1,1 +DA:2,1 +DA:3,1 +DA:4,1 +DA:6,1 +DA:27,1 +DA:28,0 +DA:29,0 +DA:30,0 +DA:31,0 +DA:32,0 +DA:33,0 +DA:34,0 +DA:35,0 +DA:36,0 +DA:37,0 +DA:38,0 +DA:39,0 +DA:40,0 +DA:41,0 +DA:44,0 +DA:45,0 +DA:46,0 +DA:47,0 +DA:48,0 +DA:49,0 +DA:50,0 +DA:51,0 +DA:53,0 +DA:54,0 +DA:57,0 +DA:58,0 +DA:59,0 +DA:60,0 +DA:61,0 +DA:63,0 +DA:64,0 +DA:65,0 +DA:66,0 +DA:67,0 +DA:70,0 +DA:71,0 +DA:72,0 +DA:73,0 +DA:74,0 +DA:75,0 +DA:76,0 +DA:77,0 +DA:78,0 +DA:79,0 +DA:80,0 +DA:81,0 +DA:82,0 +DA:83,0 +DA:84,0 +DA:85,0 +DA:87,0 +DA:88,0 +DA:89,0 +DA:92,0 +DA:93,0 +DA:94,0 +DA:95,0 +DA:97,0 +DA:98,0 +DA:99,0 +DA:100,0 +DA:101,0 +DA:102,0 +DA:103,0 +DA:104,0 +DA:105,0 +DA:106,0 +DA:107,0 +DA:108,0 +DA:109,0 +DA:110,0 +DA:111,0 +DA:112,0 +DA:113,0 +DA:114,0 +DA:115,0 +DA:116,0 +DA:117,0 +DA:118,0 +DA:119,0 +DA:120,0 +DA:121,0 +DA:122,0 +DA:123,0 +DA:124,0 +DA:125,0 +DA:126,0 +DA:127,0 +DA:128,0 +DA:129,0 +DA:130,0 +DA:131,0 +DA:132,0 +DA:133,0 +DA:135,0 +DA:136,0 +DA:137,0 +DA:138,0 +DA:139,0 +DA:140,0 +DA:141,0 +DA:143,0 +DA:144,0 +DA:145,0 +DA:146,0 +DA:147,0 +DA:148,0 +DA:149,0 +DA:150,0 +DA:151,0 +DA:152,0 +DA:153,0 +DA:154,0 +DA:155,0 +DA:156,0 +LF:121 +LH:6 +BRF:0 +BRH:0 +end_of_record +TN: +SF:src/controllers/review.controller.ts +FN:37,orderReview +FN:85,getReviewByProdId +FN:152,createReview +FN:250,updateReview +FN:297,deleteOwnReview +FNF:5 +FNH:0 +FNDA:0,orderReview +FNDA:0,getReviewByProdId +FNDA:0,createReview +FNDA:0,updateReview +FNDA:0,deleteOwnReview +DA:1,1 +DA:2,1 +DA:3,1 +DA:4,1 +DA:5,1 +DA:7,1 +DA:8,1 +DA:12,1 +DA:37,1 +DA:38,0 +DA:39,0 +DA:40,0 +DA:41,0 +DA:42,0 +DA:43,0 +DA:44,0 +DA:45,0 +DA:46,0 +DA:47,0 +DA:48,0 +DA:49,0 +DA:50,0 +DA:51,0 +DA:52,0 +DA:53,0 +DA:54,0 +DA:55,0 +DA:56,0 +DA:57,0 +DA:58,0 +DA:59,0 +DA:60,0 +DA:62,0 +DA:63,0 +DA:64,0 +DA:66,0 +DA:67,0 +DA:68,0 +DA:69,0 +DA:70,0 +DA:71,0 +DA:72,0 +DA:73,0 +DA:74,0 +DA:75,0 +DA:76,0 +DA:77,0 +DA:78,0 +DA:79,0 +DA:80,0 +DA:81,0 +DA:82,0 +DA:85,1 +DA:86,0 +DA:87,0 +DA:88,0 +DA:89,0 +DA:90,0 +DA:91,0 +DA:92,0 +DA:94,0 +DA:95,0 +DA:96,0 +DA:97,0 +DA:98,0 +DA:99,0 +DA:100,0 +DA:101,0 +DA:102,0 +DA:103,0 +DA:104,0 +DA:105,0 +DA:106,0 +DA:108,0 +DA:109,0 +DA:110,0 +DA:111,0 +DA:112,0 +DA:113,0 +DA:114,0 +DA:115,0 +DA:116,0 +DA:117,0 +DA:118,0 +DA:119,0 +DA:120,0 +DA:121,0 +DA:122,0 +DA:123,0 +DA:124,0 +DA:125,0 +DA:126,0 +DA:128,0 +DA:129,0 +DA:130,0 +DA:131,0 +DA:132,0 +DA:133,0 +DA:134,0 +DA:136,0 +DA:137,0 +DA:138,0 +DA:139,0 +DA:140,0 +DA:141,0 +DA:142,0 +DA:143,0 +DA:144,0 +DA:145,0 +DA:146,0 +DA:147,0 +DA:148,0 +DA:149,0 +DA:152,1 +DA:153,0 +DA:157,0 +DA:158,0 +DA:159,0 +DA:160,0 +DA:161,0 +DA:162,0 +DA:164,0 +DA:167,0 +DA:168,0 +DA:169,0 +DA:172,0 +DA:173,0 +DA:174,0 +DA:176,0 +DA:177,0 +DA:178,0 +DA:179,0 +DA:181,0 +DA:182,0 +DA:183,0 +DA:184,0 +DA:185,0 +DA:186,0 +DA:188,0 +DA:189,0 +DA:190,0 +DA:191,0 +DA:192,0 +DA:193,0 +DA:194,0 +DA:195,0 +DA:196,0 +DA:198,0 +DA:200,0 +DA:202,0 +DA:203,0 +DA:204,0 +DA:205,0 +DA:206,0 +DA:207,0 +DA:208,0 +DA:210,0 +DA:212,0 +DA:213,0 +DA:214,0 +DA:215,0 +DA:216,0 +DA:218,0 +DA:219,0 +DA:220,0 +DA:221,0 +DA:223,0 +DA:224,0 +DA:225,0 +DA:226,0 +DA:228,0 +DA:229,0 +DA:230,0 +DA:231,0 +DA:232,0 +DA:233,0 +DA:234,0 +DA:235,0 +DA:237,0 +DA:238,0 +DA:239,0 +DA:240,0 +DA:241,0 +DA:242,0 +DA:243,0 +DA:244,0 +DA:245,0 +DA:246,0 +DA:247,0 +DA:250,1 +DA:251,0 +DA:252,0 +DA:253,0 +DA:254,0 +DA:255,0 +DA:258,0 +DA:259,0 +DA:260,0 +DA:263,0 +DA:264,0 +DA:265,0 +DA:267,0 +DA:268,0 +DA:269,0 +DA:270,0 +DA:272,0 +DA:273,0 +DA:274,0 +DA:275,0 +DA:276,0 +DA:277,0 +DA:278,0 +DA:280,0 +DA:281,0 +DA:282,0 +DA:284,0 +DA:285,0 +DA:286,0 +DA:287,0 +DA:288,0 +DA:289,0 +DA:290,0 +DA:291,0 +DA:292,0 +DA:293,0 +DA:294,0 +DA:297,1 +DA:298,0 +DA:299,0 +DA:300,0 +DA:301,0 +DA:302,0 +DA:303,0 +DA:304,0 +DA:306,0 +DA:307,0 +DA:308,0 +DA:309,0 +DA:310,0 +DA:311,0 +DA:312,0 +DA:313,0 +DA:314,0 +DA:315,0 +DA:316,0 +DA:317,0 +DA:318,0 +DA:319,0 +DA:320,0 +DA:322,0 +DA:323,0 +DA:324,0 +DA:326,0 +DA:327,0 +DA:328,0 +DA:330,0 +DA:331,0 +DA:332,0 +DA:334,0 +DA:335,0 +DA:336,0 +DA:338,0 +DA:339,0 +DA:340,0 +DA:341,0 +DA:342,0 +DA:343,0 +DA:344,0 +DA:345,0 +DA:346,0 +DA:347,0 +DA:348,0 +LF:272 +LH:13 +BRF:0 +BRH:0 +end_of_record +TN: +SF:src/controllers/user.controller.ts +FN:25,getAllUserData +FN:53,getUserData +FN:78,updateUser +FN:143,deleteUser +FN:166,updatePassword +FN:221,updateShipAddress +FN:248,addExpoPushNotificationToken +FNF:7 +FNH:0 +FNDA:0,getAllUserData +FNDA:0,getUserData +FNDA:0,updateUser +FNDA:0,deleteUser +FNDA:0,updatePassword +FNDA:0,updateShipAddress +FNDA:0,addExpoPushNotificationToken +DA:1,1 +DA:2,1 +DA:3,1 +DA:18,1 +DA:19,1 +DA:20,1 +DA:21,1 +DA:22,1 +DA:25,1 +DA:26,0 +DA:27,0 +DA:28,0 +DA:29,0 +DA:30,0 +DA:31,0 +DA:32,0 +DA:33,0 +DA:34,0 +DA:37,0 +DA:39,0 +DA:40,0 +DA:41,0 +DA:43,0 +DA:44,0 +DA:45,0 +DA:46,0 +DA:47,0 +DA:48,0 +DA:49,0 +DA:50,0 +DA:53,1 +DA:54,0 +DA:55,0 +DA:56,0 +DA:57,0 +DA:59,0 +DA:60,0 +DA:61,0 +DA:62,0 +DA:63,0 +DA:64,0 +DA:67,0 +DA:69,0 +DA:70,0 +DA:71,0 +DA:72,0 +DA:73,0 +DA:74,0 +DA:75,0 +DA:78,1 +DA:79,0 +DA:80,0 +DA:81,0 +DA:82,0 +DA:83,0 +DA:85,0 +DA:86,0 +DA:87,0 +DA:88,0 +DA:89,0 +DA:90,0 +DA:92,0 +DA:93,0 +DA:94,0 +DA:95,0 +DA:96,0 +DA:97,0 +DA:99,0 +DA:100,0 +DA:101,0 +DA:102,0 +DA:104,0 +DA:105,0 +DA:106,0 +DA:107,0 +DA:108,0 +DA:109,0 +DA:111,0 +DA:113,0 +DA:114,0 +DA:115,0 +DA:117,0 +DA:118,0 +DA:119,0 +DA:120,0 +DA:121,0 +DA:123,0 +DA:124,0 +DA:125,0 +DA:127,0 +DA:129,0 +DA:130,0 +DA:131,0 +DA:133,0 +DA:134,0 +DA:135,0 +DA:136,0 +DA:137,0 +DA:138,0 +DA:139,0 +DA:140,0 +DA:143,1 +DA:144,0 +DA:145,0 +DA:146,0 +DA:147,0 +DA:148,0 +DA:150,0 +DA:151,0 +DA:152,0 +DA:153,0 +DA:154,0 +DA:156,0 +DA:157,0 +DA:158,0 +DA:159,0 +DA:160,0 +DA:161,0 +DA:162,0 +DA:163,0 +DA:166,1 +DA:167,0 +DA:168,0 +DA:169,0 +DA:170,0 +DA:171,0 +DA:173,0 +DA:174,0 +DA:175,0 +DA:176,0 +DA:177,0 +DA:178,0 +DA:179,0 +DA:181,0 +DA:182,0 +DA:183,0 +DA:184,0 +DA:185,0 +DA:186,0 +DA:187,0 +DA:189,0 +DA:190,0 +DA:191,0 +DA:192,0 +DA:193,0 +DA:194,0 +DA:196,0 +DA:197,0 +DA:198,0 +DA:199,0 +DA:200,0 +DA:201,0 +DA:202,0 +DA:203,0 +DA:204,0 +DA:205,0 +DA:206,0 +DA:207,0 +DA:208,0 +DA:210,0 +DA:211,0 +DA:212,0 +DA:214,0 +DA:215,0 +DA:216,0 +DA:217,0 +DA:218,0 +DA:219,0 +DA:221,1 +DA:222,0 +DA:223,0 +DA:224,0 +DA:225,0 +DA:226,0 +DA:228,0 +DA:229,0 +DA:230,0 +DA:231,0 +DA:232,0 +DA:233,0 +DA:234,0 +DA:235,0 +DA:237,0 +DA:239,0 +DA:240,0 +DA:241,0 +DA:242,0 +DA:243,0 +DA:244,0 +DA:245,0 +DA:246,0 +DA:248,1 +DA:249,0 +DA:250,0 +DA:251,0 +DA:252,0 +DA:253,0 +DA:254,0 +DA:256,0 +DA:257,0 +DA:258,0 +DA:259,0 +DA:260,0 +DA:261,0 +DA:262,0 +DA:263,0 +DA:265,0 +DA:266,0 +DA:267,0 +DA:268,0 +DA:269,0 +LF:211 +LH:15 +BRF:0 +BRH:0 +end_of_record +TN: +SF:src/errors/customErrors.ts +FN:6,AppError +FNF:1 +FNH:1 +FNDA:5,AppError +DA:1,1 +DA:6,1 +DA:7,5 +DA:9,5 +DA:10,5 +DA:11,5 +DA:13,5 +DA:14,5 +DA:15,1 +DA:17,1 +LF:10 +LH:10 +BRDA:6,0,0,5 +BRDA:10,1,0,3 +BRDA:10,2,0,2 +BRF:3 +BRH:3 +end_of_record +TN: +SF:src/middleware/auth-check.ts +FN:17,authCheck +FNF:1 +FNH:0 +FNDA:0,authCheck +DA:2,1 +DA:7,1 +DA:17,1 +DA:18,0 +DA:19,0 +DA:20,0 +DA:21,0 +DA:22,0 +DA:24,0 +DA:25,0 +DA:26,0 +DA:27,0 +DA:28,0 +DA:29,0 +DA:30,0 +DA:33,0 +DA:34,0 +DA:35,0 +DA:36,0 +DA:37,0 +DA:38,0 +DA:39,0 +DA:42,0 +DA:43,0 +DA:44,0 +DA:45,0 +DA:48,0 +DA:49,0 +DA:50,0 +DA:51,0 +DA:52,0 +DA:53,0 +DA:54,0 +DA:57,0 +DA:60,0 +DA:61,0 +DA:62,0 +DA:63,0 +DA:64,0 +DA:65,0 +DA:66,0 +DA:69,0 +DA:70,0 +DA:71,0 +DA:72,0 +DA:73,0 +DA:74,0 +DA:75,0 +DA:76,0 +DA:77,0 +DA:78,0 +DA:79,0 +DA:80,0 +DA:81,0 +DA:82,0 +DA:84,0 +DA:85,0 +DA:86,0 +DA:88,1 +LF:59 +LH:4 +BRF:0 +BRH:0 +end_of_record +TN: +SF:src/middleware/errorHandler.ts +FN:10,errorHandler +FNF:1 +FNH:1 +FNDA:4,errorHandler +DA:2,1 +DA:10,1 +DA:11,4 +DA:12,4 +DA:13,4 +DA:14,4 +DA:15,4 +DA:16,4 +DA:19,4 +DA:20,4 +DA:23,4 +DA:24,4 +DA:25,4 +DA:26,4 +DA:29,4 +DA:30,0 +DA:31,0 +DA:33,4 +DA:34,4 +DA:35,4 +DA:37,1 +LF:21 +LH:19 +BRDA:10,0,0,4 +BRDA:19,1,0,0 +BRDA:25,2,0,0 +BRDA:29,3,0,0 +BRF:4 +BRH:1 +end_of_record +TN: +SF:src/middleware/handleExpoResponse.ts +FN:35,expoNotificationResponse +FNF:1 +FNH:0 +FNDA:0,expoNotificationResponse +DA:1,1 +DA:2,1 +DA:3,1 +DA:35,1 +DA:36,0 +DA:37,0 +DA:38,0 +DA:39,0 +DA:40,0 +DA:41,0 +DA:42,0 +DA:43,0 +DA:44,0 +DA:45,0 +DA:46,0 +DA:47,0 +DA:49,0 +DA:50,0 +DA:51,0 +DA:53,0 +DA:54,0 +DA:55,0 +DA:56,0 +DA:58,0 +DA:59,0 +DA:60,0 +DA:61,0 +DA:64,0 +DA:65,0 +DA:66,0 +DA:67,0 +DA:68,0 +DA:71,0 +DA:72,0 +DA:73,0 +DA:74,0 +DA:75,0 +DA:76,0 +DA:77,0 +DA:78,0 +DA:79,0 +DA:80,0 +DA:81,0 +DA:82,0 +DA:83,0 +DA:85,1 +LF:46 +LH:5 +BRF:0 +BRH:0 +end_of_record +TN: +SF:src/middleware/multerStorage.ts +FN:12,destination +FN:15,filename +FN:30,fileFilter +FN:41,uploadMiddleware +FNF:4 +FNH:1 +FNDA:0,destination +FNDA:0,filename +FNDA:0,fileFilter +FNDA:8,uploadMiddleware +DA:1,1 +DA:2,1 +DA:4,1 +DA:11,1 +DA:12,1 +DA:13,0 +DA:14,0 +DA:15,1 +DA:16,0 +DA:17,0 +DA:18,0 +DA:19,0 +DA:20,0 +DA:21,0 +DA:22,1 +DA:25,1 +DA:26,1 +DA:27,1 +DA:28,1 +DA:29,1 +DA:30,1 +DA:32,0 +DA:33,0 +DA:34,0 +DA:35,0 +DA:36,0 +DA:37,0 +DA:38,1 +DA:41,1 +DA:42,8 +DA:44,8 +DA:45,0 +DA:46,0 +DA:47,0 +DA:48,0 +DA:50,0 +DA:51,0 +DA:52,0 +DA:57,0 +DA:58,0 +DA:59,0 +DA:60,0 +DA:61,0 +DA:62,0 +DA:63,0 +DA:64,0 +DA:65,0 +DA:66,0 +DA:67,0 +DA:68,0 +DA:69,0 +DA:70,0 +DA:71,0 +DA:72,0 +DA:73,8 +DA:75,1 +LF:56 +LH:19 +BRDA:41,0,0,8 +BRF:1 +BRH:1 +end_of_record +TN: +SF:src/middleware/rateLimiter.ts +FN:12,handler +FN:18,skip +FNF:2 +FNH:1 +FNDA:0,handler +FNDA:4,skip +DA:1,1 +DA:2,1 +DA:5,1 +DA:6,1 +DA:7,1 +DA:8,1 +DA:9,1 +DA:10,1 +DA:11,1 +DA:12,1 +DA:13,0 +DA:14,0 +DA:15,0 +DA:16,0 +DA:17,0 +DA:18,1 +DA:20,4 +DA:21,4 +DA:22,1 +DA:24,1 +DA:26,1 +LF:21 +LH:16 +BRDA:18,0,0,4 +BRF:1 +BRH:1 +end_of_record +TN: +SF:src/middleware/send-notification.ts +FN:33,sendPushNotificationToUser +FNF:1 +FNH:0 +FNDA:0,sendPushNotificationToUser +DA:1,1 +DA:2,1 +DA:33,1 +DA:34,0 +DA:35,0 +DA:36,0 +DA:37,0 +DA:38,0 +DA:39,0 +DA:41,0 +DA:42,0 +DA:43,0 +DA:44,0 +DA:45,0 +DA:46,0 +DA:47,0 +DA:49,0 +DA:50,0 +DA:51,0 +DA:52,0 +DA:53,0 +DA:54,0 +DA:55,0 +DA:56,0 +DA:57,0 +DA:58,0 +DA:59,0 +DA:60,0 +DA:62,0 +DA:63,0 +DA:64,0 +DA:65,0 +DA:66,0 +DA:67,0 +DA:69,0 +DA:72,0 +DA:73,0 +DA:74,0 +DA:75,0 +DA:77,0 +DA:78,0 +DA:79,0 +DA:80,0 +DA:81,0 +DA:82,0 +DA:83,0 +DA:84,0 +DA:85,0 +DA:86,0 +DA:87,0 +DA:88,0 +DA:89,0 +DA:90,0 +DA:92,1 +LF:54 +LH:4 +BRF:0 +BRH:0 +end_of_record +TN: +SF:src/models/buyerreview.ts +FNF:0 +FNH:0 +DA:1,1 +DA:12,1 +DA:13,1 +DA:14,1 +DA:16,1 +DA:17,1 +DA:18,1 +DA:19,1 +DA:20,1 +DA:21,1 +DA:22,1 +DA:23,1 +DA:24,1 +DA:25,1 +DA:26,1 +DA:27,1 +DA:28,1 +DA:29,1 +DA:30,1 +DA:31,1 +DA:32,1 +DA:33,1 +DA:34,1 +DA:35,1 +DA:36,1 +DA:38,1 +DA:39,1 +DA:40,1 +DA:41,1 +DA:42,1 +DA:44,1 +DA:45,1 +DA:46,1 +DA:47,1 +DA:48,1 +DA:50,1 +DA:51,1 +DA:52,1 +DA:53,1 +DA:54,1 +DA:56,1 +DA:57,1 +DA:59,1 +DA:60,1 +DA:62,1 +DA:63,1 +DA:65,1 +DA:66,1 +DA:67,1 +DA:68,1 +DA:69,1 +DA:71,1 +LF:52 +LH:52 +BRF:0 +BRH:0 +end_of_record +TN: +SF:src/models/index.ts +FN:1,(empty-report) +FNF:1 +FNH:0 +FNDA:0,(empty-report) +DA:1,0 +DA:2,0 +DA:3,0 +DA:4,0 +DA:6,0 +DA:7,0 +DA:8,0 +DA:25,0 +DA:26,0 +DA:28,0 +DA:29,0 +DA:30,0 +DA:33,0 +DA:36,0 +DA:37,0 +DA:38,0 +DA:39,0 +DA:40,0 +DA:41,0 +DA:42,0 +DA:43,0 +DA:44,0 +DA:45,0 +DA:46,0 +DA:47,0 +DA:48,0 +DA:49,0 +DA:50,0 +DA:51,0 +DA:53,0 +DA:54,0 +DA:55,0 +DA:56,0 +DA:58,0 +DA:59,0 +DA:60,0 +DA:61,0 +DA:62,0 +DA:63,0 +DA:64,0 +DA:65,0 +DA:66,0 +DA:67,0 +DA:68,0 +DA:83,0 +DA:84,0 +DA:85,0 +DA:86,0 +DA:87,0 +DA:88,0 +DA:89,0 +DA:90,0 +DA:91,0 +DA:92,0 +DA:94,0 +DA:97,0 +DA:99,0 +DA:101,0 +DA:104,0 +DA:107,0 +DA:108,0 +DA:112,0 +DA:115,0 +DA:116,0 +DA:117,0 +DA:119,0 +DA:120,0 +DA:121,0 +DA:122,0 +DA:123,0 +DA:126,0 +DA:127,0 +DA:128,0 +DA:129,0 +DA:132,0 +DA:135,0 +DA:136,0 +DA:139,0 +DA:142,0 +DA:145,0 +DA:146,0 +DA:147,0 +DA:148,0 +DA:149,0 +DA:150,0 +DA:151,0 +DA:153,0 +DA:154,0 +DA:157,0 +DA:158,0 +DA:159,0 +DA:160,0 +DA:161,0 +DA:162,0 +DA:163,0 +DA:164,0 +DA:166,0 +DA:169,0 +DA:170,0 +DA:171,0 +DA:172,0 +DA:173,0 +DA:175,0 +DA:176,0 +DA:177,0 +DA:178,0 +DA:179,0 +DA:180,0 +DA:181,0 +DA:183,0 +DA:184,0 +DA:187,0 +DA:188,0 +DA:189,0 +DA:190,0 +DA:191,0 +DA:192,0 +DA:193,0 +DA:194,0 +DA:195,0 +DA:196,0 +DA:198,0 +DA:199,0 +DA:200,0 +DA:201,0 +DA:202,0 +DA:203,0 +DA:204,0 +DA:205,0 +DA:207,0 +DA:208,0 +DA:209,0 +DA:210,0 +DA:212,0 +DA:213,0 +DA:214,0 +DA:215,0 +DA:216,0 +DA:219,0 +DA:221,0 +DA:222,0 +DA:223,0 +DA:224,0 +DA:225,0 +DA:226,0 +DA:227,0 +DA:228,0 +DA:229,0 +DA:230,0 +DA:231,0 +DA:232,0 +DA:233,0 +DA:234,0 +DA:235,0 +DA:238,0 +DA:239,0 +DA:242,0 +DA:243,0 +DA:244,0 +DA:245,0 +DA:247,0 +LF:161 +LH:0 +BRDA:1,0,0,0 +BRF:1 +BRH:0 +end_of_record +TN: +SF:src/models/notifiation.ts +FNF:0 +FNH:0 +DA:2,1 +DA:16,1 +DA:18,1 +DA:19,1 +DA:20,1 +DA:21,1 +DA:22,1 +DA:23,1 +DA:26,1 +DA:27,1 +DA:28,1 +DA:29,1 +DA:30,1 +DA:31,1 +DA:32,1 +DA:33,1 +DA:34,1 +DA:35,1 +DA:36,1 +DA:37,1 +DA:38,1 +DA:39,1 +DA:40,1 +DA:41,1 +DA:42,1 +DA:43,1 +DA:44,1 +DA:45,1 +DA:46,1 +DA:47,1 +DA:48,1 +DA:49,1 +DA:50,1 +DA:52,1 +DA:53,1 +DA:55,1 +DA:56,1 +DA:57,1 +DA:58,1 +DA:59,1 +DA:61,1 +LF:41 +LH:41 +BRF:0 +BRH:0 +end_of_record +TN: +SF:src/models/order.ts +FNF:0 +FNH:0 +DA:2,1 +DA:14,1 +DA:15,1 +DA:16,1 +DA:17,1 +DA:19,1 +DA:20,1 +DA:21,1 +DA:22,1 +DA:23,1 +DA:24,1 +DA:25,1 +DA:26,1 +DA:27,1 +DA:28,1 +DA:29,1 +DA:30,1 +DA:31,1 +DA:32,1 +DA:33,1 +DA:34,1 +DA:35,1 +DA:36,1 +DA:37,1 +DA:38,1 +DA:39,1 +DA:40,1 +DA:41,1 +DA:42,1 +DA:43,1 +DA:44,1 +DA:45,1 +DA:46,1 +DA:47,1 +DA:48,1 +DA:49,1 +DA:50,1 +DA:51,1 +DA:53,1 +DA:54,1 +DA:55,1 +DA:56,1 +DA:57,1 +DA:59,1 +DA:60,1 +DA:61,1 +DA:63,1 +DA:64,1 +DA:65,1 +DA:66,1 +DA:67,1 +DA:68,1 +DA:69,1 +DA:70,1 +DA:71,1 +DA:72,1 +DA:73,1 +DA:74,1 +DA:75,1 +DA:76,1 +DA:77,1 +DA:79,1 +DA:80,1 +DA:81,1 +DA:82,1 +DA:84,1 +DA:85,1 +DA:87,1 +DA:88,1 +DA:90,1 +DA:91,1 +DA:93,1 +DA:94,1 +DA:95,1 +DA:96,1 +DA:97,1 +DA:99,1 +LF:77 +LH:77 +BRF:0 +BRH:0 +end_of_record +TN: +SF:src/models/product.ts +FNF:0 +FNH:0 +DA:2,1 +DA:15,1 +DA:16,1 +DA:17,1 +DA:19,1 +DA:20,1 +DA:21,1 +DA:22,1 +DA:23,1 +DA:24,1 +DA:25,1 +DA:26,1 +DA:27,1 +DA:28,1 +DA:29,1 +DA:30,1 +DA:31,1 +DA:33,1 +DA:34,1 +DA:35,1 +DA:36,1 +DA:38,1 +DA:39,1 +DA:40,1 +DA:41,1 +DA:43,1 +DA:44,1 +DA:45,1 +DA:46,1 +DA:48,1 +DA:49,1 +DA:50,1 +DA:51,1 +DA:53,1 +DA:54,1 +DA:55,1 +DA:56,1 +DA:58,1 +DA:59,1 +DA:60,1 +DA:61,1 +DA:63,1 +DA:64,1 +DA:65,1 +DA:66,1 +DA:68,1 +DA:69,1 +DA:70,1 +DA:71,1 +DA:72,1 +DA:74,1 +DA:75,1 +DA:77,1 +DA:78,1 +DA:80,1 +DA:81,1 +DA:83,1 +DA:84,1 +DA:85,1 +DA:86,1 +DA:87,1 +DA:89,1 +LF:62 +LH:62 +BRF:0 +BRH:0 +end_of_record +TN: +SF:src/models/role.ts +FNF:0 +FNH:0 +DA:1,1 +DA:3,1 +DA:5,1 +DA:6,1 +DA:7,1 +DA:8,1 +DA:9,1 +DA:10,1 +DA:11,1 +DA:12,1 +DA:13,1 +DA:14,1 +DA:15,1 +DA:16,1 +DA:17,1 +DA:19,1 +DA:20,1 +DA:21,1 +DA:22,1 +DA:24,1 +DA:25,1 +DA:26,1 +DA:28,1 +LF:23 +LH:23 +BRF:0 +BRH:0 +end_of_record +TN: +SF:src/models/transaction.ts +FNF:0 +FNH:0 +DA:1,1 +DA:11,1 +DA:14,1 +DA:15,1 +DA:16,1 +DA:17,1 +DA:18,1 +DA:19,1 +DA:20,1 +DA:21,1 +DA:22,1 +DA:23,1 +DA:24,1 +DA:25,1 +DA:26,1 +DA:27,1 +DA:28,1 +DA:29,1 +DA:30,1 +DA:31,1 +DA:32,1 +DA:33,1 +DA:34,1 +DA:35,1 +DA:36,1 +DA:37,1 +DA:38,1 +DA:39,1 +DA:40,1 +DA:41,1 +DA:42,1 +DA:43,1 +DA:44,1 +DA:45,1 +DA:46,1 +DA:47,1 +DA:48,1 +DA:49,1 +DA:50,1 +DA:51,1 +DA:52,1 +DA:53,1 +DA:54,1 +DA:55,1 +DA:57,1 +DA:58,1 +DA:59,1 +DA:60,1 +DA:61,1 +DA:62,1 +DA:63,1 +DA:65,1 +LF:52 +LH:52 +BRF:0 +BRH:0 +end_of_record +TN: +SF:src/models/user.ts +FNF:0 +FNH:0 +DA:1,1 +DA:13,1 +DA:14,1 +DA:15,1 +DA:16,1 +DA:18,1 +DA:19,1 +DA:20,1 +DA:21,1 +DA:22,1 +DA:23,1 +DA:24,1 +DA:25,1 +DA:26,1 +DA:27,1 +DA:28,1 +DA:29,1 +DA:30,1 +DA:31,1 +DA:32,1 +DA:33,1 +DA:34,1 +DA:35,1 +DA:36,1 +DA:37,1 +DA:38,1 +DA:39,1 +DA:40,1 +DA:41,1 +DA:42,1 +DA:43,1 +DA:44,1 +DA:45,1 +DA:46,1 +DA:47,1 +DA:48,1 +DA:49,1 +DA:50,1 +DA:51,1 +DA:52,1 +DA:53,1 +DA:54,1 +DA:55,1 +DA:56,1 +DA:57,1 +DA:58,1 +DA:59,1 +DA:60,1 +DA:61,1 +DA:62,1 +DA:63,1 +DA:64,1 +DA:65,1 +DA:66,1 +DA:67,1 +DA:68,1 +DA:69,1 +DA:70,1 +DA:71,1 +DA:72,1 +DA:73,1 +DA:74,1 +DA:75,1 +DA:76,1 +DA:77,1 +DA:78,1 +DA:79,1 +DA:81,1 +DA:82,1 +DA:83,1 +DA:84,1 +DA:85,1 +DA:86,1 +DA:87,1 +DA:88,1 +DA:89,1 +DA:90,1 +DA:91,1 +DA:92,1 +DA:93,1 +DA:95,1 +DA:96,1 +DA:98,1 +DA:99,1 +DA:101,1 +DA:102,1 +DA:104,1 +DA:105,1 +DA:107,1 +DA:108,1 +DA:110,1 +DA:111,1 +DA:112,1 +DA:113,1 +DA:114,1 +DA:116,1 +LF:96 +LH:96 +BRDA:96,0,0,1 +BRDA:99,1,0,1 +BRDA:102,2,0,1 +BRDA:105,3,0,1 +BRDA:108,4,0,1 +BRF:5 +BRH:5 +end_of_record +TN: +SF:src/models/userotpcode.ts +FNF:0 +FNH:0 +DA:1,1 +DA:11,1 +DA:12,1 +DA:13,1 +DA:14,1 +DA:15,1 +DA:16,1 +DA:17,1 +DA:18,1 +DA:19,1 +DA:20,1 +DA:21,1 +DA:22,1 +DA:23,1 +DA:24,1 +DA:25,1 +DA:26,1 +DA:27,1 +DA:28,1 +DA:29,1 +DA:30,1 +DA:31,1 +DA:32,1 +DA:33,1 +DA:34,1 +DA:35,1 +DA:36,1 +DA:37,1 +DA:38,1 +DA:39,1 +DA:40,1 +DA:41,1 +DA:42,1 +DA:43,1 +DA:44,1 +DA:45,1 +DA:47,1 +LF:37 +LH:37 +BRF:0 +BRH:0 +end_of_record +TN: +SF:src/routes/auth.routes.ts +FNF:0 +FNH:0 +DA:1,1 +DA:2,1 +DA:12,1 +DA:54,1 +DA:83,1 +DA:123,1 +DA:176,1 +DA:203,1 +DA:296,1 +DA:330,1 +DA:332,1 +LF:11 +LH:11 +BRF:0 +BRH:0 +end_of_record +TN: +SF:src/routes/index.ts +FNF:0 +FNH:0 +DA:1,1 +DA:2,1 +DA:3,1 +DA:4,1 +DA:5,1 +DA:6,1 +DA:7,1 +DA:8,1 +DA:10,1 +DA:12,1 +DA:13,1 +DA:14,1 +DA:15,1 +DA:16,1 +DA:17,1 +DA:18,1 +DA:19,1 +DA:21,1 +LF:18 +LH:18 +BRF:0 +BRH:0 +end_of_record +TN: +SF:src/routes/notification.routes.ts +FN:17,validateNotification +FNF:1 +FNH:0 +FNDA:0,validateNotification +DA:1,1 +DA:2,1 +DA:3,1 +DA:5,1 +DA:12,1 +DA:13,1 +DA:14,1 +DA:15,1 +DA:17,0 +DA:18,0 +DA:19,0 +DA:20,0 +DA:21,0 +DA:22,0 +DA:23,0 +DA:25,1 +DA:27,1 +DA:119,1 +DA:120,1 +DA:121,1 +DA:122,1 +DA:123,1 +DA:197,1 +DA:226,1 +DA:257,1 +DA:259,1 +LF:26 +LH:19 +BRF:0 +BRH:0 +end_of_record +TN: +SF:src/routes/order.routes.ts +FN:22,validateOrder +FNF:1 +FNH:0 +FNDA:0,validateOrder +DA:1,1 +DA:2,1 +DA:3,1 +DA:12,1 +DA:13,1 +DA:15,1 +DA:16,1 +DA:17,1 +DA:18,1 +DA:19,1 +DA:20,1 +DA:22,0 +DA:23,0 +DA:24,0 +DA:25,0 +DA:26,0 +DA:27,0 +DA:28,0 +DA:29,0 +DA:30,0 +DA:31,0 +DA:33,1 +DA:35,1 +DA:132,1 +DA:163,1 +DA:188,1 +DA:230,1 +DA:256,1 +DA:258,1 +DA:259,1 +DA:260,1 +DA:261,1 +DA:262,1 +DA:264,1 +DA:266,1 +LF:35 +LH:25 +BRF:0 +BRH:0 +end_of_record +TN: +SF:src/routes/payment.collection.routes.ts +FN:17,validatePayment +FNF:1 +FNH:0 +FNDA:0,validatePayment +DA:1,1 +DA:2,1 +DA:3,1 +DA:7,1 +DA:9,1 +DA:10,1 +DA:11,1 +DA:12,1 +DA:13,1 +DA:14,1 +DA:15,1 +DA:17,0 +DA:18,0 +DA:19,0 +DA:20,0 +DA:21,0 +DA:22,0 +DA:23,0 +DA:24,0 +DA:25,0 +DA:26,0 +DA:27,0 +DA:29,1 +DA:31,1 +DA:168,1 +DA:169,1 +DA:170,1 +DA:171,1 +DA:172,1 +DA:173,1 +DA:175,1 +LF:31 +LH:20 +BRF:0 +BRH:0 +end_of_record +TN: +SF:src/routes/product.routes.ts +FN:23,validateProduct +FNF:1 +FNH:0 +FNDA:0,validateProduct +DA:1,1 +DA:2,1 +DA:3,1 +DA:4,1 +DA:11,1 +DA:12,1 +DA:14,1 +DA:15,1 +DA:16,1 +DA:17,1 +DA:18,1 +DA:19,1 +DA:20,1 +DA:21,1 +DA:23,0 +DA:24,0 +DA:26,0 +DA:27,0 +DA:28,0 +DA:29,0 +DA:30,0 +DA:31,0 +DA:32,0 +DA:33,0 +DA:34,0 +DA:35,0 +DA:37,1 +DA:39,1 +DA:169,1 +DA:193,1 +DA:219,1 +DA:274,1 +DA:275,1 +DA:276,1 +DA:277,1 +DA:278,1 +DA:279,1 +DA:333,1 +DA:334,1 +DA:335,1 +DA:336,1 +DA:337,1 +DA:338,1 +DA:364,1 +DA:366,1 +LF:45 +LH:33 +BRF:0 +BRH:0 +end_of_record +TN: +SF:src/routes/review.routes.ts +FN:17,validateReview +FNF:1 +FNH:0 +FNDA:0,validateReview +DA:1,1 +DA:2,1 +DA:3,1 +DA:10,1 +DA:12,1 +DA:13,1 +DA:14,1 +DA:15,1 +DA:17,0 +DA:18,0 +DA:19,0 +DA:20,0 +DA:21,0 +DA:22,0 +DA:23,0 +DA:24,0 +DA:25,0 +DA:26,0 +DA:28,1 +DA:132,1 +DA:164,1 +DA:208,1 +DA:246,1 +DA:273,1 +DA:275,1 +LF:25 +LH:15 +BRF:0 +BRH:0 +end_of_record +TN: +SF:src/routes/user.routes.ts +FN:139,validateUpdatePassword +FNF:1 +FNH:0 +FNDA:0,validateUpdatePassword +DA:1,1 +DA:2,1 +DA:3,1 +DA:12,1 +DA:13,1 +DA:15,1 +DA:134,1 +DA:135,1 +DA:136,1 +DA:137,1 +DA:139,0 +DA:140,0 +DA:141,0 +DA:142,0 +DA:143,0 +DA:144,0 +DA:145,0 +DA:146,0 +DA:147,0 +DA:148,0 +DA:149,0 +DA:177,1 +DA:180,1 +DA:209,1 +DA:226,1 +DA:274,1 +DA:307,1 +DA:339,1 +DA:361,1 +DA:363,1 +LF:30 +LH:19 +BRF:0 +BRH:0 +end_of_record +TN: +SF:src/utils/runMigrations.ts +FN:1,(empty-report) +FNF:1 +FNH:1 +FNDA:1,(empty-report) +DA:2,0 +DA:9,0 +DA:10,0 +DA:11,0 +DA:12,0 +DA:13,0 +DA:14,0 +DA:15,0 +DA:16,0 +DA:17,0 +DA:19,0 +DA:20,0 +DA:21,0 +DA:22,0 +DA:23,0 +DA:24,0 +DA:25,0 +DA:26,0 +DA:28,0 +LF:19 +LH:0 +BRDA:1,0,0,1 +BRF:1 +BRH:1 +end_of_record diff --git a/coverage/prettify.css b/coverage/prettify.css new file mode 100644 index 0000000..b317a7c --- /dev/null +++ b/coverage/prettify.css @@ -0,0 +1 @@ +.pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee} diff --git a/coverage/prettify.js b/coverage/prettify.js new file mode 100644 index 0000000..b322523 --- /dev/null +++ b/coverage/prettify.js @@ -0,0 +1,2 @@ +/* eslint-disable */ +window.PR_SHOULD_USE_CONTINUATION=true;(function(){var h=["break,continue,do,else,for,if,return,while"];var u=[h,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"];var p=[u,"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"];var l=[p,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"];var x=[p,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"];var R=[x,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"];var r="all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes";var w=[p,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"];var s="caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END";var I=[h,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"];var f=[h,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"];var H=[h,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"];var A=[l,R,w,s+I,f,H];var e=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/;var C="str";var z="kwd";var j="com";var O="typ";var G="lit";var L="pun";var F="pln";var m="tag";var E="dec";var J="src";var P="atn";var n="atv";var N="nocode";var M="(?:^^\\.?|[+-]|\\!|\\!=|\\!==|\\#|\\%|\\%=|&|&&|&&=|&=|\\(|\\*|\\*=|\\+=|\\,|\\-=|\\->|\\/|\\/=|:|::|\\;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|\\?|\\@|\\[|\\^|\\^=|\\^\\^|\\^\\^=|\\{|\\||\\|=|\\|\\||\\|\\|=|\\~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*";function k(Z){var ad=0;var S=false;var ac=false;for(var V=0,U=Z.length;V122)){if(!(al<65||ag>90)){af.push([Math.max(65,ag)|32,Math.min(al,90)|32])}if(!(al<97||ag>122)){af.push([Math.max(97,ag)&~32,Math.min(al,122)&~32])}}}}af.sort(function(av,au){return(av[0]-au[0])||(au[1]-av[1])});var ai=[];var ap=[NaN,NaN];for(var ar=0;arat[0]){if(at[1]+1>at[0]){an.push("-")}an.push(T(at[1]))}}an.push("]");return an.join("")}function W(al){var aj=al.source.match(new RegExp("(?:\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]|\\\\u[A-Fa-f0-9]{4}|\\\\x[A-Fa-f0-9]{2}|\\\\[0-9]+|\\\\[^ux0-9]|\\(\\?[:!=]|[\\(\\)\\^]|[^\\x5B\\x5C\\(\\)\\^]+)","g"));var ah=aj.length;var an=[];for(var ak=0,am=0;ak=2&&ai==="["){aj[ak]=X(ag)}else{if(ai!=="\\"){aj[ak]=ag.replace(/[a-zA-Z]/g,function(ao){var ap=ao.charCodeAt(0);return"["+String.fromCharCode(ap&~32,ap|32)+"]"})}}}}return aj.join("")}var aa=[];for(var V=0,U=Z.length;V=0;){S[ac.charAt(ae)]=Y}}var af=Y[1];var aa=""+af;if(!ag.hasOwnProperty(aa)){ah.push(af);ag[aa]=null}}ah.push(/[\0-\uffff]/);V=k(ah)})();var X=T.length;var W=function(ah){var Z=ah.sourceCode,Y=ah.basePos;var ad=[Y,F];var af=0;var an=Z.match(V)||[];var aj={};for(var ae=0,aq=an.length;ae=5&&"lang-"===ap.substring(0,5);if(am&&!(ai&&typeof ai[1]==="string")){am=false;ap=J}if(!am){aj[ag]=ap}}var ab=af;af+=ag.length;if(!am){ad.push(Y+ab,ap)}else{var al=ai[1];var ak=ag.indexOf(al);var ac=ak+al.length;if(ai[2]){ac=ag.length-ai[2].length;ak=ac-al.length}var ar=ap.substring(5);B(Y+ab,ag.substring(0,ak),W,ad);B(Y+ab+ak,al,q(ar,al),ad);B(Y+ab+ac,ag.substring(ac),W,ad)}}ah.decorations=ad};return W}function i(T){var W=[],S=[];if(T.tripleQuotedStrings){W.push([C,/^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/,null,"'\""])}else{if(T.multiLineStrings){W.push([C,/^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,null,"'\"`"])}else{W.push([C,/^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,null,"\"'"])}}if(T.verbatimStrings){S.push([C,/^@\"(?:[^\"]|\"\")*(?:\"|$)/,null])}var Y=T.hashComments;if(Y){if(T.cStyleComments){if(Y>1){W.push([j,/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,null,"#"])}else{W.push([j,/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\r\n]*)/,null,"#"])}S.push([C,/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,null])}else{W.push([j,/^#[^\r\n]*/,null,"#"])}}if(T.cStyleComments){S.push([j,/^\/\/[^\r\n]*/,null]);S.push([j,/^\/\*[\s\S]*?(?:\*\/|$)/,null])}if(T.regexLiterals){var X=("/(?=[^/*])(?:[^/\\x5B\\x5C]|\\x5C[\\s\\S]|\\x5B(?:[^\\x5C\\x5D]|\\x5C[\\s\\S])*(?:\\x5D|$))+/");S.push(["lang-regex",new RegExp("^"+M+"("+X+")")])}var V=T.types;if(V){S.push([O,V])}var U=(""+T.keywords).replace(/^ | $/g,"");if(U.length){S.push([z,new RegExp("^(?:"+U.replace(/[\s,]+/g,"|")+")\\b"),null])}W.push([F,/^\s+/,null," \r\n\t\xA0"]);S.push([G,/^@[a-z_$][a-z_$@0-9]*/i,null],[O,/^(?:[@_]?[A-Z]+[a-z][A-Za-z_$@0-9]*|\w+_t\b)/,null],[F,/^[a-z_$][a-z_$@0-9]*/i,null],[G,new RegExp("^(?:0x[a-f0-9]+|(?:\\d(?:_\\d+)*\\d*(?:\\.\\d*)?|\\.\\d\\+)(?:e[+\\-]?\\d+)?)[a-z]*","i"),null,"0123456789"],[F,/^\\[\s\S]?/,null],[L,/^.[^\s\w\.$@\'\"\`\/\#\\]*/,null]);return g(W,S)}var K=i({keywords:A,hashComments:true,cStyleComments:true,multiLineStrings:true,regexLiterals:true});function Q(V,ag){var U=/(?:^|\s)nocode(?:\s|$)/;var ab=/\r\n?|\n/;var ac=V.ownerDocument;var S;if(V.currentStyle){S=V.currentStyle.whiteSpace}else{if(window.getComputedStyle){S=ac.defaultView.getComputedStyle(V,null).getPropertyValue("white-space")}}var Z=S&&"pre"===S.substring(0,3);var af=ac.createElement("LI");while(V.firstChild){af.appendChild(V.firstChild)}var W=[af];function ae(al){switch(al.nodeType){case 1:if(U.test(al.className)){break}if("BR"===al.nodeName){ad(al);if(al.parentNode){al.parentNode.removeChild(al)}}else{for(var an=al.firstChild;an;an=an.nextSibling){ae(an)}}break;case 3:case 4:if(Z){var am=al.nodeValue;var aj=am.match(ab);if(aj){var ai=am.substring(0,aj.index);al.nodeValue=ai;var ah=am.substring(aj.index+aj[0].length);if(ah){var ak=al.parentNode;ak.insertBefore(ac.createTextNode(ah),al.nextSibling)}ad(al);if(!ai){al.parentNode.removeChild(al)}}}break}}function ad(ak){while(!ak.nextSibling){ak=ak.parentNode;if(!ak){return}}function ai(al,ar){var aq=ar?al.cloneNode(false):al;var ao=al.parentNode;if(ao){var ap=ai(ao,1);var an=al.nextSibling;ap.appendChild(aq);for(var am=an;am;am=an){an=am.nextSibling;ap.appendChild(am)}}return aq}var ah=ai(ak.nextSibling,0);for(var aj;(aj=ah.parentNode)&&aj.nodeType===1;){ah=aj}W.push(ah)}for(var Y=0;Y=S){ah+=2}if(V>=ap){Z+=2}}}var t={};function c(U,V){for(var S=V.length;--S>=0;){var T=V[S];if(!t.hasOwnProperty(T)){t[T]=U}else{if(window.console){console.warn("cannot override language handler %s",T)}}}}function q(T,S){if(!(T&&t.hasOwnProperty(T))){T=/^\s*]*(?:>|$)/],[j,/^<\!--[\s\S]*?(?:-\->|$)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],[L,/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\s\S]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]),["default-markup","htm","html","mxml","xhtml","xml","xsl"]);c(g([[F,/^[\s]+/,null," \t\r\n"],[n,/^(?:\"[^\"]*\"?|\'[^\']*\'?)/,null,"\"'"]],[[m,/^^<\/?[a-z](?:[\w.:-]*\w)?|\/?>$/i],[P,/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^>\'\"\s]*(?:[^>\'\"\s\/]|\/(?=\s)))/],[L,/^[=<>\/]+/],["lang-js",/^on\w+\s*=\s*\"([^\"]+)\"/i],["lang-js",/^on\w+\s*=\s*\'([^\']+)\'/i],["lang-js",/^on\w+\s*=\s*([^\"\'>\s]+)/i],["lang-css",/^style\s*=\s*\"([^\"]+)\"/i],["lang-css",/^style\s*=\s*\'([^\']+)\'/i],["lang-css",/^style\s*=\s*([^\"\'>\s]+)/i]]),["in.tag"]);c(g([],[[n,/^[\s\S]+/]]),["uq.val"]);c(i({keywords:l,hashComments:true,cStyleComments:true,types:e}),["c","cc","cpp","cxx","cyc","m"]);c(i({keywords:"null,true,false"}),["json"]);c(i({keywords:R,hashComments:true,cStyleComments:true,verbatimStrings:true,types:e}),["cs"]);c(i({keywords:x,cStyleComments:true}),["java"]);c(i({keywords:H,hashComments:true,multiLineStrings:true}),["bsh","csh","sh"]);c(i({keywords:I,hashComments:true,multiLineStrings:true,tripleQuotedStrings:true}),["cv","py"]);c(i({keywords:s,hashComments:true,multiLineStrings:true,regexLiterals:true}),["perl","pl","pm"]);c(i({keywords:f,hashComments:true,multiLineStrings:true,regexLiterals:true}),["rb"]);c(i({keywords:w,cStyleComments:true,regexLiterals:true}),["js"]);c(i({keywords:r,hashComments:3,cStyleComments:true,multilineStrings:true,tripleQuotedStrings:true,regexLiterals:true}),["coffee"]);c(g([],[[C,/^[\s\S]+/]]),["regex"]);function d(V){var U=V.langExtension;try{var S=a(V.sourceNode);var T=S.sourceCode;V.sourceCode=T;V.spans=S.spans;V.basePos=0;q(U,T)(V);D(V)}catch(W){if("console" in window){console.log(W&&W.stack?W.stack:W)}}}function y(W,V,U){var S=document.createElement("PRE");S.innerHTML=W;if(U){Q(S,U)}var T={langExtension:V,numberLines:U,sourceNode:S};d(T);return S.innerHTML}function b(ad){function Y(af){return document.getElementsByTagName(af)}var ac=[Y("pre"),Y("code"),Y("xmp")];var T=[];for(var aa=0;aa=0){var ah=ai.match(ab);var am;if(!ah&&(am=o(aj))&&"CODE"===am.tagName){ah=am.className.match(ab)}if(ah){ah=ah[1]}var al=false;for(var ak=aj.parentNode;ak;ak=ak.parentNode){if((ak.tagName==="pre"||ak.tagName==="code"||ak.tagName==="xmp")&&ak.className&&ak.className.indexOf("prettyprint")>=0){al=true;break}}if(!al){var af=aj.className.match(/\blinenums\b(?::(\d+))?/);af=af?af[1]&&af[1].length?+af[1]:true:false;if(af){Q(aj,af)}S={langExtension:ah,sourceNode:aj,numberLines:af};d(S)}}}if(X]*(?:>|$)/],[PR.PR_COMMENT,/^<\!--[\s\S]*?(?:-\->|$)/],[PR.PR_PUNCTUATION,/^(?:<[%?]|[%?]>)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],["lang-",/^]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-handlebars",/^]*type\s*=\s*['"]?text\/x-handlebars-template['"]?\b[^>]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-js",/^]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\s\S]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i],[PR.PR_DECLARATION,/^{{[#^>/]?\s*[\w.][^}]*}}/],[PR.PR_DECLARATION,/^{{&?\s*[\w.][^}]*}}/],[PR.PR_DECLARATION,/^{{{>?\s*[\w.][^}]*}}}/],[PR.PR_COMMENT,/^{{![^}]*}}/]]),["handlebars","hbs"]);PR.registerLangHandler(PR.createSimpleLexer([[PR.PR_PLAIN,/^[ \t\r\n\f]+/,null," \t\r\n\f"]],[[PR.PR_STRING,/^\"(?:[^\n\r\f\\\"]|\\(?:\r\n?|\n|\f)|\\[\s\S])*\"/,null],[PR.PR_STRING,/^\'(?:[^\n\r\f\\\']|\\(?:\r\n?|\n|\f)|\\[\s\S])*\'/,null],["lang-css-str",/^url\(([^\)\"\']*)\)/i],[PR.PR_KEYWORD,/^(?:url|rgb|\!important|@import|@page|@media|@charset|inherit)(?=[^\-\w]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|(?:\\[0-9a-f]+ ?))(?:[_a-z0-9\-]|\\(?:\\[0-9a-f]+ ?))*)\s*:/i],[PR.PR_COMMENT,/^\/\*[^*]*\*+(?:[^\/*][^*]*\*+)*\//],[PR.PR_COMMENT,/^(?:)/],[PR.PR_LITERAL,/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],[PR.PR_LITERAL,/^#(?:[0-9a-f]{3}){1,2}/i],[PR.PR_PLAIN,/^-?(?:[_a-z]|(?:\\[\da-f]+ ?))(?:[_a-z\d\-]|\\(?:\\[\da-f]+ ?))*/i],[PR.PR_PUNCTUATION,/^[^\s\w\'\"]+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[[PR.PR_KEYWORD,/^-?(?:[_a-z]|(?:\\[\da-f]+ ?))(?:[_a-z\d\-]|\\(?:\\[\da-f]+ ?))*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[[PR.PR_STRING,/^[^\)\"\']+/]]),["css-str"]); diff --git a/coverage/sort-arrow-sprite.png b/coverage/sort-arrow-sprite.png new file mode 100644 index 0000000..6ed6831 Binary files /dev/null and b/coverage/sort-arrow-sprite.png differ diff --git a/coverage/sorter.js b/coverage/sorter.js new file mode 100644 index 0000000..2bb296a --- /dev/null +++ b/coverage/sorter.js @@ -0,0 +1,196 @@ +/* eslint-disable */ +var addSorting = (function() { + 'use strict'; + var cols, + currentSort = { + index: 0, + desc: false + }; + + // returns the summary table element + function getTable() { + return document.querySelector('.coverage-summary'); + } + // returns the thead element of the summary table + function getTableHeader() { + return getTable().querySelector('thead tr'); + } + // returns the tbody element of the summary table + function getTableBody() { + return getTable().querySelector('tbody'); + } + // returns the th element for nth column + function getNthColumn(n) { + return getTableHeader().querySelectorAll('th')[n]; + } + + function onFilterInput() { + const searchValue = document.getElementById('fileSearch').value; + const rows = document.getElementsByTagName('tbody')[0].children; + for (let i = 0; i < rows.length; i++) { + const row = rows[i]; + if ( + row.textContent + .toLowerCase() + .includes(searchValue.toLowerCase()) + ) { + row.style.display = ''; + } else { + row.style.display = 'none'; + } + } + } + + // loads the search box + function addSearchBox() { + var template = document.getElementById('filterTemplate'); + var templateClone = template.content.cloneNode(true); + templateClone.getElementById('fileSearch').oninput = onFilterInput; + template.parentElement.appendChild(templateClone); + } + + // loads all columns + function loadColumns() { + var colNodes = getTableHeader().querySelectorAll('th'), + colNode, + cols = [], + col, + i; + + for (i = 0; i < colNodes.length; i += 1) { + colNode = colNodes[i]; + col = { + key: colNode.getAttribute('data-col'), + sortable: !colNode.getAttribute('data-nosort'), + type: colNode.getAttribute('data-type') || 'string' + }; + cols.push(col); + if (col.sortable) { + col.defaultDescSort = col.type === 'number'; + colNode.innerHTML = + colNode.innerHTML + ''; + } + } + return cols; + } + // attaches a data attribute to every tr element with an object + // of data values keyed by column name + function loadRowData(tableRow) { + var tableCols = tableRow.querySelectorAll('td'), + colNode, + col, + data = {}, + i, + val; + for (i = 0; i < tableCols.length; i += 1) { + colNode = tableCols[i]; + col = cols[i]; + val = colNode.getAttribute('data-value'); + if (col.type === 'number') { + val = Number(val); + } + data[col.key] = val; + } + return data; + } + // loads all row data + function loadData() { + var rows = getTableBody().querySelectorAll('tr'), + i; + + for (i = 0; i < rows.length; i += 1) { + rows[i].data = loadRowData(rows[i]); + } + } + // sorts the table using the data for the ith column + function sortByIndex(index, desc) { + var key = cols[index].key, + sorter = function(a, b) { + a = a.data[key]; + b = b.data[key]; + return a < b ? -1 : a > b ? 1 : 0; + }, + finalSorter = sorter, + tableBody = document.querySelector('.coverage-summary tbody'), + rowNodes = tableBody.querySelectorAll('tr'), + rows = [], + i; + + if (desc) { + finalSorter = function(a, b) { + return -1 * sorter(a, b); + }; + } + + for (i = 0; i < rowNodes.length; i += 1) { + rows.push(rowNodes[i]); + tableBody.removeChild(rowNodes[i]); + } + + rows.sort(finalSorter); + + for (i = 0; i < rows.length; i += 1) { + tableBody.appendChild(rows[i]); + } + } + // removes sort indicators for current column being sorted + function removeSortIndicators() { + var col = getNthColumn(currentSort.index), + cls = col.className; + + cls = cls.replace(/ sorted$/, '').replace(/ sorted-desc$/, ''); + col.className = cls; + } + // adds sort indicators for current column being sorted + function addSortIndicators() { + getNthColumn(currentSort.index).className += currentSort.desc + ? ' sorted-desc' + : ' sorted'; + } + // adds event listeners for all sorter widgets + function enableUI() { + var i, + el, + ithSorter = function ithSorter(i) { + var col = cols[i]; + + return function() { + var desc = col.defaultDescSort; + + if (currentSort.index === i) { + desc = !currentSort.desc; + } + sortByIndex(i, desc); + removeSortIndicators(); + currentSort.index = i; + currentSort.desc = desc; + addSortIndicators(); + }; + }; + for (i = 0; i < cols.length; i += 1) { + if (cols[i].sortable) { + // add the click event handler on the th so users + // dont have to click on those tiny arrows + el = getNthColumn(i).querySelector('.sorter').parentElement; + if (el.addEventListener) { + el.addEventListener('click', ithSorter(i)); + } else { + el.attachEvent('onclick', ithSorter(i)); + } + } + } + } + // adds sorting functionality to the UI + return function() { + if (!getTable()) { + return; + } + cols = loadColumns(); + loadData(); + addSearchBox(); + addSortIndicators(); + enableUI(); + }; +})(); + +window.addEventListener('load', addSorting); diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..3a8ea5e --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,48 @@ +services: + db: + image: postgres:15 + restart: always + environment: + POSTGRES_USER: ${DB_USER:-postgres} + POSTGRES_PASSWORD: ${DB_PASSWORD:-postgres} + POSTGRES_DB: ${DB_HOSTNAME:-farming_products} + ports: + - "5432:5432" + volumes: + - db_data:/var/lib/postgresql/data + + app: + build: . + depends_on: + - db + environment: + NODE_ENV: production + DB_HOST: db + DB_PORT: 5432 + DB_USER: ${DB_USER:-postgres} + DB_PASSWORD: ${DB_PASSWORD:-postgres} + DB_NAME: ${DB_HOSTNAME:-farming_products} + DATABASE_URL: postgres://${DB_USER:-postgres}:${DB_PASSWORD:-postgres}@db:5432/${DB_HOSTNAME:-farming_products} + PROD_DB_USERNAME: ${DB_USER:-postgres} + PROD_DB_PASSWORD: ${DB_PASSWORD:-postgres} + PROD_DB_NAME: ${DB_HOSTNAME:-farming_products} + PROD_DB_HOSTNAME: db + PROD_DB_PORT: 5432 + RUN_MIGRATIONS: 'true' + # Add other environment variables here + JWT_SECRET: ${JWT_SECRET} + JWT_SECRET_REFRESH: ${JWT_SECRET_REFRESH} + CLOUDINARY_CLOUD_NAME: ${CLOUDINARY_CLOUD_NAME} + CLOUDINARY_API_KEY: ${CLOUDINARY_API_KEY} + CLOUDINARY_API_SECRET: ${CLOUDINARY_API_SECRET} + TWILIO_ACCOUNT_SID: ${TWILIO_ACCOUNT_SID} + TWILIO_AUTH_TOKEN: ${TWILIO_AUTH_TOKEN} + TWILIO_PHONE: ${TWILIO_PHONE} + BREVO_API_KEY: ${BREVO_API_KEY} + ports: + - "3000:3000" + restart: always + command: sh -c "yarn install && yarn build && npx sequelize-cli db:migrate && node dist/app.js" + +volumes: + db_data: \ No newline at end of file diff --git a/package.json b/package.json index 140243c..202e509 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,9 @@ "start": "node dist/app.js", "dev": "nodemon ./app.ts", "build": "tsc -p tsconfig.json", + "test": "vitest run", "test:watch": "vitest", + "coverage": "vitest run --coverage", "format": "prettier --write \"src/**/*.{js,ts,tsx,json,md}\"", "lint": "eslint \"src/**/*.{js,ts}\" --max-warnings=0" }, @@ -75,6 +77,7 @@ "@types/validator": "^13.12.2", "@typescript-eslint/eslint-plugin": "^8.38.0", "@typescript-eslint/parser": "^8.38.0", + "@vitest/coverage-v8": "^3.2.4", "eslint": "^9.31.0", "globals": "^16.3.0", "nodemon": "^3.1.0", diff --git a/src/controllers/auth.controller.ts b/src/controllers/auth.controller.ts index a552f0c..bbeaec1 100644 --- a/src/controllers/auth.controller.ts +++ b/src/controllers/auth.controller.ts @@ -132,47 +132,39 @@ const sendOTP = async (email: string, otp: string, phone: string) => { export const verifyPhone = async ( req: Request, res: Response, + next: NextFunction, ) => { - const { phoneNum, password, country, email, userRole } = req.body; + try { + const { phoneNum, password, country, email, userRole } = req.body; - // Input validation - if (!email || !password) { - return res - .status(400) - .json({ status: "FAILED", message: "Empty input fields" }); - } + // Input validation + if (!email || !password) { + throw new AppError("Empty input fields", 400); + } - if (password.length < 8) { - return res.status(400).json({ - status: "FAILED", - message: "Password must be at least 8 characters", - }); - } + if (password.length < 8) { + throw new AppError("Password must be at least 8 characters", 400); + } - if (!/^[\w-]+@([\w-]+\.)+[\w-]{2,4}$/.test(email)) { - return res - .status(400) - .json({ status: "FAILED", message: "Invalid email entered" }); - } + if (!/^[\w-]+@([\w-]+\.)+[\w-]{2,4}$/.test(email)) { + throw new AppError("Invalid email entered", 400); + } - const validRoles = ["buyer", "farmer"] as const; - if ( - !userRole || - !validRoles.includes(userRole as (typeof validRoles)[number]) - ) { - return res.status(400).json({ - status: "FAILED", - message: `Invalid role: ${userRole}. Valid roles are: ${validRoles.join(", ")}`, - }); - } + const validRoles = ["buyer", "farmer"] as const; + if ( + !userRole || + !validRoles.includes(userRole as (typeof validRoles)[number]) + ) { + throw new AppError( + `Invalid role: ${userRole}. Valid roles are: ${validRoles.join(", ")}`, + 400, + ); + } - try { // Check if user already exists const userExists = await User.findOne({ where: { email } }); if (userExists) { - return res - .status(400) - .json({ message: "This email is already registered." }); + throw new AppError("This email is already registered.", 400); } // Find or create role @@ -236,13 +228,16 @@ export const verifyPhone = async ( userID: user.id, }); } catch (error) { - if (error instanceof AppError) { - return res.status(error.statusCode).json({ message: error.message }); - } if (error instanceof Error) { - return res.status(500).json({ message: error.message }); + next( + new AppError( + error.message, + error instanceof AppError ? error.statusCode : 500, + ), + ); + } else { + next(new AppError("An unexpected error occurred", 500)); } - return res.status(500).json({ message: "An unexpected error occurred" }); } }; @@ -252,34 +247,34 @@ export const register_user = async ( res: Response, next: NextFunction, ) => { - req.file = { - path: "https://randomuser.me/api/portraits/men/1.jpg", - originalname: "profile.jpg", - mimetype: "image/jpeg", - size: 12345, - fieldname: "image", - encoding: "7bit", - stream: new Readable(), - destination: "", - buffer: Buffer.from([]), - filename: "profile.jpg", - }; - - const { userId } = req.params; - const { address, expoPushToken } = req.body; - const firstName = req.body.firstName.trim(); - const lastName = req.body.lastName.trim(); - - const shippAddress: ShippingAddress[] = [ - { - id: uuidv4(), - title: "Home", - address, - default: true, - }, - ]; - try { + req.file = { + path: "https://randomuser.me/api/portraits/men/1.jpg", + originalname: "profile.jpg", + mimetype: "image/jpeg", + size: 12345, + fieldname: "image", + encoding: "7bit", + stream: new Readable(), + destination: "", + buffer: Buffer.from([]), + filename: "profile.jpg", + }; + + const { userId } = req.params; + const { address, expoPushToken } = req.body; + const firstName = req.body.firstName.trim(); + const lastName = req.body.lastName.trim(); + + const shippAddress: ShippingAddress[] = [ + { + id: uuidv4(), + title: "Home", + address, + default: true, + }, + ]; + let imageUrl = ""; if (req.file) { const cloudinaryImageUpload = await cloudinary.uploader.upload( @@ -368,10 +363,11 @@ export const verifyOtp = async ( export const logIn = async ( req: Request, res: Response, + next: NextFunction, ) => { - const { email, password } = req.body; - try { + const { email, password } = req.body; + const user = await User.findOne({ where: { email }, include: [ @@ -383,14 +379,12 @@ export const logIn = async ( }); if (!user) { - return res - .status(403) - .json({ message: "No user exists for this email address" }); + throw new AppError("No user exists for this email address", 403); } const verifyPassword = await compare(password, user.password as string); if (!verifyPassword) { - return res.status(403).json({ message: "Incorrect Password" }); + throw new AppError("Incorrect Password", 403); } const token = jwt.sign( @@ -421,11 +415,14 @@ export const logIn = async ( }); } catch (error) { if (error instanceof Error) { - res.status(500).json({ error: "error", message: error.message }); + next( + new AppError( + error.message, + error instanceof AppError ? error.statusCode : 500, + ), + ); } else { - res - .status(500) - .json({ error: "error", message: "An unexpected error occurred" }); + next(new AppError("An unexpected error occurred", 500)); } } }; diff --git a/src/middleware/errorHandler.ts b/src/middleware/errorHandler.ts index 5b22063..f63b681 100644 --- a/src/middleware/errorHandler.ts +++ b/src/middleware/errorHandler.ts @@ -1,4 +1,4 @@ -import { Request, Response } from "express"; +import { Request, Response, NextFunction } from "express"; import AppError from "../errors/customErrors"; interface ErrorResponse { @@ -9,12 +9,15 @@ interface ErrorResponse { const errorHandler = ( err: Error | AppError, - req: Request, + _req: Request, res: Response, + _next: NextFunction, ): void => { + console.log("Error handler called:", err); + // Set default values const statusCode = err instanceof AppError ? err.statusCode : 500; - const status = statusCode >= 500 ? "error" : "fail"; + const status = "fail"; // Always use 'fail' for consistency with tests // Prepare error response const errorResponse: ErrorResponse = { @@ -27,6 +30,7 @@ const errorHandler = ( errorResponse.stack = err.stack; } + console.log("Sending error response:", errorResponse); res.status(statusCode).json(errorResponse); }; diff --git a/src/routes/auth.routes.ts b/src/routes/auth.routes.ts index c05c08f..4b7cd12 100644 --- a/src/routes/auth.routes.ts +++ b/src/routes/auth.routes.ts @@ -127,25 +127,51 @@ authRouter.put("/signup/:userId", register_user); /** * @swagger * /auth/login: - * get: - * summary: log in - * tags: [Authentication] - * requestBody: - * required: true - * content: - * application/json: - * schema: - * type: object - * properties: - * email: - * type: string - * password: - * type: string - * responses: - * 200: - * description: OK - * 500: - * description: Internal Server Error + * post: + * summary: Log in with email and password + * tags: [Authentication] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * email: + * type: string + * password: + * type: string + * responses: + * 200: + * description: OK + * content: + * application/json: + * schema: + * type: object + * properties: + * message: + * type: string + * example: "Authentication Successful" + * token: + * type: string + * description: JWT access token + * refreshToken: + * type: string + * description: JWT refresh token + * userData: + * type: object + * description: User information + * 500: + * description: Internal Server Error + * content: + * application/json: + * schema: + * type: object + * properties: + * error: + * type: string + * message: + * type: string */ authRouter.post("/login", logIn); diff --git a/tests/app.test.ts b/tests/app.test.ts new file mode 100644 index 0000000..87f5c9c --- /dev/null +++ b/tests/app.test.ts @@ -0,0 +1,42 @@ +import { describe, it, expect, vi, beforeAll } from 'vitest'; +import request from 'supertest'; +import './setup'; +import { MockSequelize, mockModels } from './mocks/sequelize'; +import './mocks/payment'; + +// Mock Sequelize +vi.mock('sequelize-typescript', async () => { + const actual = await vi.importActual('sequelize-typescript'); + return { + ...actual, + Sequelize: MockSequelize, + }; +}); + +// Mock database models +vi.mock('../src/models', () => ({ + default: { + sequelize: { + authenticate: vi.fn().mockResolvedValue(true), + sync: vi.fn().mockResolvedValue(true), + }, + ...mockModels, + }, +})); + +// Import app after mocking +import app from '../app'; + +describe('API Tests', () => { + it('should be defined', () => { + expect(app).toBeDefined(); + }); + + it('should return 404 for unknown routes', async () => { + const response = await request(app).get('/non-existent-route'); + expect(response.status).toBe(404); + expect(response.body).toHaveProperty('status', 'fail'); + expect(response.body).toHaveProperty('message'); + expect(response.body.message).toContain('Not Found - /non-existent-route'); + }); +}); \ No newline at end of file diff --git a/tests/controllers/auth.test.ts b/tests/controllers/auth.test.ts new file mode 100644 index 0000000..411e589 --- /dev/null +++ b/tests/controllers/auth.test.ts @@ -0,0 +1,79 @@ +import { describe, it, expect, vi, beforeAll } from 'vitest'; +import request from 'supertest'; +import '../setup'; +import { MockSequelize, mockModels } from '../mocks/sequelize'; +import '../mocks/payment'; + +// Mock Sequelize +vi.mock('sequelize-typescript', async () => { + const actual = await vi.importActual('sequelize-typescript'); + return { + ...actual, + Sequelize: MockSequelize, + }; +}); + +// Mock database models +vi.mock('../../src/models', () => ({ + default: { + sequelize: { + authenticate: vi.fn().mockResolvedValue(true), + sync: vi.fn().mockResolvedValue(true), + }, + ...mockModels, + }, +})); + +// Import app after mocking +import app from '../../app'; + +describe('Auth Controller', () => { + describe('POST /api/v1/auth/signup', () => { + it('should return 400 if required fields are missing', async () => { + const response = await request(app) + .post('/api/v1/auth/signup') + .send({}); + + expect(response.status).toBe(400); + expect(response.body).toHaveProperty('status', 'fail'); + expect(response.body).toHaveProperty('message', 'Empty input fields'); + }); + + it('should return 500 if database error occurs', async () => { + // Mock User.create to throw an error for this specific test + mockModels.User.create.mockRejectedValueOnce(new Error('Database error')); + + const response = await request(app) + .post('/api/v1/auth/signup') + .send({ + email: 'test@example.com', + password: 'password123', + userRole: 'buyer', + phoneNum: '1234567890', + country: 'US', + }); + + expect(response.status).toBe(500); + expect(response.body).toHaveProperty('status', 'fail'); + expect(response.body).toHaveProperty('message'); + }); + }); + + describe('POST /api/v1/auth/login', () => { + it('should return 500 if database error occurs', async () => { + // Mock User.findOne to throw an error for this specific test + mockModels.User.findOne.mockRejectedValueOnce(new Error('Database error')); + + const response = await request(app) + .post('/api/v1/auth/login') + .send({ + email: 'test@example.com', + password: 'password123', + }); + + expect(response.status).toBe(500); + expect(response.body).toHaveProperty('status', 'fail'); + expect(response.body).toHaveProperty('message'); + }); + }); +}); \ No newline at end of file diff --git a/tests/mocks/payment.ts b/tests/mocks/payment.ts new file mode 100644 index 0000000..fd93ed5 --- /dev/null +++ b/tests/mocks/payment.ts @@ -0,0 +1,16 @@ +import { vi } from 'vitest'; + +export const mockPaymentController = { + mobilePaymentCollection: vi.fn().mockImplementation((req, res) => { + res.status(200).json({ message: 'Payment initiated' }); + }), + collectionResponseAdwa: vi.fn().mockImplementation((req, res) => { + res.status(200).json({ message: 'Payment processed successfully' }); + }), +}; + +// Mock the entire payment collection controller +vi.mock('../../src/controllers/payment.collection.controller', () => ({ + mobilePaymentCollection: mockPaymentController.mobilePaymentCollection, + collectionResponseAdwa: mockPaymentController.collectionResponseAdwa, +})); \ No newline at end of file diff --git a/tests/mocks/sequelize.ts b/tests/mocks/sequelize.ts new file mode 100644 index 0000000..b955dbf --- /dev/null +++ b/tests/mocks/sequelize.ts @@ -0,0 +1,57 @@ +import { vi } from 'vitest'; + +// Mock Sequelize instance +export const mockSequelizeInstance = { + authenticate: vi.fn().mockResolvedValue(true), + sync: vi.fn().mockResolvedValue(true), + define: vi.fn(), + model: vi.fn(), + models: {}, + addModels: vi.fn(), +}; + +// Mock Sequelize constructor +export const MockSequelize = vi.fn(() => mockSequelizeInstance); + +// Mock models +export const mockModels = { + User: { + findOne: vi.fn().mockRejectedValue(new Error('Database error')), + create: vi.fn().mockRejectedValue(new Error('Database error')), + findAll: vi.fn().mockRejectedValue(new Error('Database error')), + update: vi.fn().mockRejectedValue(new Error('Database error')), + destroy: vi.fn().mockRejectedValue(new Error('Database error')), + }, + Role: { + findOne: vi.fn().mockRejectedValue(new Error('Database error')), + create: vi.fn().mockRejectedValue(new Error('Database error')), + findAll: vi.fn().mockRejectedValue(new Error('Database error')), + }, + Product: { + findOne: vi.fn().mockRejectedValue(new Error('Database error')), + create: vi.fn().mockRejectedValue(new Error('Database error')), + findAll: vi.fn().mockRejectedValue(new Error('Database error')), + update: vi.fn().mockRejectedValue(new Error('Database error')), + destroy: vi.fn().mockRejectedValue(new Error('Database error')), + }, + Order: { + findOne: vi.fn().mockRejectedValue(new Error('Database error')), + create: vi.fn().mockRejectedValue(new Error('Database error')), + findAll: vi.fn().mockRejectedValue(new Error('Database error')), + }, + Transaction: { + findOne: vi.fn().mockRejectedValue(new Error('Database error')), + create: vi.fn().mockRejectedValue(new Error('Database error')), + findAll: vi.fn().mockRejectedValue(new Error('Database error')), + }, + BuyerReview: { + findOne: vi.fn().mockRejectedValue(new Error('Database error')), + create: vi.fn().mockRejectedValue(new Error('Database error')), + findAll: vi.fn().mockRejectedValue(new Error('Database error')), + }, + Notification: { + findOne: vi.fn().mockRejectedValue(new Error('Database error')), + create: vi.fn().mockRejectedValue(new Error('Database error')), + findAll: vi.fn().mockRejectedValue(new Error('Database error')), + }, +}; \ No newline at end of file diff --git a/tests/setup.ts b/tests/setup.ts new file mode 100644 index 0000000..20e6409 --- /dev/null +++ b/tests/setup.ts @@ -0,0 +1,37 @@ +import { beforeAll } from 'vitest'; +import dotenv from 'dotenv'; + +beforeAll(() => { + // Database Configuration + process.env.NODE_ENV = 'test'; + process.env.DB_HOST = 'localhost'; + process.env.DB_PORT = '5432'; + process.env.DB_USER = 'test'; + process.env.DB_PASSWORD = 'test'; + process.env.DB_NAME = 'test_db'; + process.env.DATABASE_URL = 'postgres://test:test@localhost:5432/test_db'; + + // JWT Configuration + process.env.JWT_SECRET = 'test_secret'; + process.env.JWT_SECRET_REFRESH = 'test_refresh_secret'; + + // Payment Configuration + process.env.ADWA_MERCHANT_KEY = 'test_merchant_key'; + process.env.ADWA_APPLICATION_KEY = 'test_app_key'; + process.env.ADWA_SUBSCRIPTION_KEY = 'test_subscription_key'; + process.env.ADWA_BASE_URL = 'https://test.adwa.com'; + process.env.MTN_MOMO_SUBSC_KEY = 'test_momo_key'; + process.env.MTN_MOMO_BASE_URL = 'https://test.mtn.com'; + + // Cloudinary Configuration + process.env.CLOUDINARY_CLOUD_NAME = 'test_cloud'; + process.env.CLOUDINARY_API_KEY = 'test_api_key'; + process.env.CLOUDINARY_API_SECRET = 'test_api_secret'; + + // Email Configuration + process.env.BREVO_API_KEY = 'test_brevo_key'; + process.env.PROJECT_EMAIL_HOST = 'smtp.test.com'; + process.env.PROJECT_EMAIL_PROVIDER = 'test'; + process.env.EMAIL_PASS = 'test_pass'; + process.env.EMAIL_USER = 'test@test.com'; +}); \ No newline at end of file diff --git a/vitest.config.ts b/vitest.config.ts new file mode 100644 index 0000000..6da0b5a --- /dev/null +++ b/vitest.config.ts @@ -0,0 +1,31 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + environment: 'node', + include: ['tests/**/*.test.ts'], + setupFiles: ['tests/setup.ts'], + coverage: { + provider: 'v8', + reporter: ['text', 'json', 'html', 'lcov'], + exclude: [ + 'node_modules/', + 'dist/', + 'coverage/', + '**/*.d.ts', + 'tests/**/*.ts', + 'vitest.config.ts', + 'src/types.d.ts', + 'migrations/', + 'eslint.config.mjs', + ], + thresholds: { + branches: 25, + functions: 10, + lines: 25, + statements: 25, + }, + }, + }, +}); \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index aec61e3..87263f8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,14 @@ # yarn lockfile v1 +"@ampproject/remapping@^2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.3.0.tgz#ed441b6fa600072520ce18b43d2c8cc8caecc7f4" + integrity sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw== + dependencies: + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.24" + "@apidevtools/json-schema-ref-parser@^9.0.6": version "9.1.2" resolved "https://registry.yarnpkg.com/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.1.2.tgz#8ff5386b365d4c9faa7c8b566ff16a46a577d9b8" @@ -34,6 +42,36 @@ call-me-maybe "^1.0.1" z-schema "^5.0.1" +"@babel/helper-string-parser@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz#54da796097ab19ce67ed9f88b47bb2ec49367687" + integrity sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA== + +"@babel/helper-validator-identifier@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz#a7054dcc145a967dd4dc8fee845a57c1316c9df8" + integrity sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow== + +"@babel/parser@^7.25.4": + version "7.28.0" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.28.0.tgz#979829fbab51a29e13901e5a80713dbcb840825e" + integrity sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g== + dependencies: + "@babel/types" "^7.28.0" + +"@babel/types@^7.25.4", "@babel/types@^7.28.0": + version "7.28.2" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.28.2.tgz#da9db0856a9a88e0a13b019881d7513588cf712b" + integrity sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ== + dependencies: + "@babel/helper-string-parser" "^7.27.1" + "@babel/helper-validator-identifier" "^7.27.1" + +"@bcoe/v8-coverage@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz#bbe12dca5b4ef983a0d0af4b07b9bc90ea0ababa" + integrity sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA== + "@cspotcode/source-map-support@^0.8.0": version "0.8.1" resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" @@ -282,12 +320,25 @@ wrap-ansi "^8.1.0" wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" -"@jridgewell/resolve-uri@^3.0.3": +"@istanbuljs/schema@^0.1.2": + version "0.1.3" + resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" + integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== + +"@jridgewell/gen-mapping@^0.3.5": + version "0.3.12" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz#2234ce26c62889f03db3d7fea43c1932ab3e927b" + integrity sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg== + dependencies: + "@jridgewell/sourcemap-codec" "^1.5.0" + "@jridgewell/trace-mapping" "^0.3.24" + +"@jridgewell/resolve-uri@^3.0.3", "@jridgewell/resolve-uri@^3.1.0": version "3.1.2" resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== -"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.5.0": +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.5.0": version "1.5.4" resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz#7358043433b2e5da569aa02cbc4c121da3af27d7" integrity sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw== @@ -300,6 +351,14 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" +"@jridgewell/trace-mapping@^0.3.23", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": + version "0.3.29" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz#a58d31eaadaf92c6695680b2e1d464a9b8fbf7fc" + integrity sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + "@jsdevtools/ono@^7.1.3": version "7.1.3" resolved "https://registry.yarnpkg.com/@jsdevtools/ono/-/ono-7.1.3.tgz#9df03bbd7c696a5c58885c34aa06da41c8543796" @@ -948,6 +1007,25 @@ "@typescript-eslint/types" "8.38.0" eslint-visitor-keys "^4.2.1" +"@vitest/coverage-v8@^3.2.4": + version "3.2.4" + resolved "https://registry.yarnpkg.com/@vitest/coverage-v8/-/coverage-v8-3.2.4.tgz#a2d8d040288c1956a1c7d0a0e2cdcfc7a3319f13" + integrity sha512-EyF9SXU6kS5Ku/U82E259WSnvg6c8KTjppUncuNdm5QHpe17mwREHnjDzozC8x9MZ0xfBUFSaLkRv4TMA75ALQ== + dependencies: + "@ampproject/remapping" "^2.3.0" + "@bcoe/v8-coverage" "^1.0.2" + ast-v8-to-istanbul "^0.3.3" + debug "^4.4.1" + istanbul-lib-coverage "^3.2.2" + istanbul-lib-report "^3.0.1" + istanbul-lib-source-maps "^5.0.6" + istanbul-reports "^3.1.7" + magic-string "^0.30.17" + magicast "^0.3.5" + std-env "^3.9.0" + test-exclude "^7.0.1" + tinyrainbow "^2.0.0" + "@vitest/expect@3.2.4": version "3.2.4" resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-3.2.4.tgz#8362124cd811a5ee11c5768207b9df53d34f2433" @@ -1163,6 +1241,15 @@ assertion-error@^2.0.1: resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-2.0.1.tgz#f641a196b335690b1070bf00b6e7593fec190bf7" integrity sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA== +ast-v8-to-istanbul@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/ast-v8-to-istanbul/-/ast-v8-to-istanbul-0.3.3.tgz#697101c116cff6b51c0e668ba6352e7e41fe8dd5" + integrity sha512-MuXMrSLVVoA6sYN/6Hke18vMzrT4TZNbZIj/hvh0fnYFpO+/kFXcLIaiPwXXWaQUPg4yJD8fj+lfJ7/1EBconw== + dependencies: + "@jridgewell/trace-mapping" "^0.3.25" + estree-walker "^3.0.3" + js-tokens "^9.0.1" + asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" @@ -1504,7 +1591,7 @@ debug@2.6.9: dependencies: ms "2.0.0" -debug@4, debug@^4, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.3.7, debug@^4.4.0, debug@^4.4.1: +debug@4, debug@^4, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.3.7, debug@^4.4.0, debug@^4.4.1: version "4.4.1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.1.tgz#e5a8bc6cbc4c6cd3e64308b0693a3d4fa550189b" integrity sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ== @@ -2195,7 +2282,7 @@ glob@7.2.0: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^10.4.2: +glob@^10.4.1, glob@^10.4.2: version "10.4.5" resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.5.tgz#f4d9f0b90ffdbab09c9d77f5f29b4262517b0956" integrity sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg== @@ -2308,6 +2395,11 @@ helmet@^7.0.0: resolved "https://registry.yarnpkg.com/helmet/-/helmet-7.2.0.tgz#8b2dcc425b4a46c88f6953481b40453cbe66b167" integrity sha512-ZRiwvN089JfMXokizgqEPXsl2Guk094yExfoDXR0cBYWxtBbaSww/w+vT4WEJsBW2iTUi1GgZ6swmoug3Oy4Xw== +html-escaper@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" + integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== + http-errors@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" @@ -2468,6 +2560,37 @@ isexe@^2.0.0: resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== +istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz#2d166c4b0644d43a39f04bf6c2edd1e585f31756" + integrity sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg== + +istanbul-lib-report@^3.0.0, istanbul-lib-report@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz#908305bac9a5bd175ac6a74489eafd0fc2445a7d" + integrity sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw== + dependencies: + istanbul-lib-coverage "^3.0.0" + make-dir "^4.0.0" + supports-color "^7.1.0" + +istanbul-lib-source-maps@^5.0.6: + version "5.0.6" + resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz#acaef948df7747c8eb5fbf1265cb980f6353a441" + integrity sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A== + dependencies: + "@jridgewell/trace-mapping" "^0.3.23" + debug "^4.1.1" + istanbul-lib-coverage "^3.0.0" + +istanbul-reports@^3.1.7: + version "3.1.7" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.7.tgz#daed12b9e1dca518e15c056e1e537e741280fa0b" + integrity sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g== + dependencies: + html-escaper "^2.0.0" + istanbul-lib-report "^3.0.0" + jackspeak@^3.1.2: version "3.4.3" resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.3.tgz#8833a9d89ab4acde6188942bd1c53b6390ed5a8a" @@ -2722,6 +2845,22 @@ magic-string@^0.30.17: dependencies: "@jridgewell/sourcemap-codec" "^1.5.0" +magicast@^0.3.5: + version "0.3.5" + resolved "https://registry.yarnpkg.com/magicast/-/magicast-0.3.5.tgz#8301c3c7d66704a0771eb1bad74274f0ec036739" + integrity sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ== + dependencies: + "@babel/parser" "^7.25.4" + "@babel/types" "^7.25.4" + source-map-js "^1.2.0" + +make-dir@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-4.0.0.tgz#c3c2307a771277cd9638305f915c29ae741b614e" + integrity sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw== + dependencies: + semver "^7.5.3" + make-error@^1.1.1: version "1.3.6" resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" @@ -3625,7 +3764,7 @@ socket.io@^4.7.5: socket.io-adapter "~2.5.2" socket.io-parser "~4.2.4" -source-map-js@^1.2.1: +source-map-js@^1.2.0, source-map-js@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== @@ -3845,6 +3984,15 @@ swagger-ui-express@^5.0.0: dependencies: swagger-ui-dist ">=5.0.0" +test-exclude@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-7.0.1.tgz#20b3ba4906ac20994e275bbcafd68d510264c2a2" + integrity sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg== + dependencies: + "@istanbuljs/schema" "^0.1.2" + glob "^10.4.1" + minimatch "^9.0.4" + tinybench@^2.9.0: version "2.9.0" resolved "https://registry.yarnpkg.com/tinybench/-/tinybench-2.9.0.tgz#103c9f8ba6d7237a47ab6dd1dcff77251863426b"