{"id":25338124,"url":"https://github.com/boobagreen/investmentapi","last_synced_at":"2026-04-14T14:31:55.686Z","repository":{"id":249450734,"uuid":"831530814","full_name":"boobaGreen/investmentAPI","owner":"boobaGreen","description":"node js - express - typescript - test - postman - project","archived":false,"fork":false,"pushed_at":"2024-07-31T21:20:49.000Z","size":881,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-06-06T09:38:31.697Z","etag":null,"topics":["api","crud","express","jest","jwt","nodejs","prisma","supertest","typescript"],"latest_commit_sha":null,"homepage":"https://investmentapi.onrender.com/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/boobaGreen.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-07-20T20:43:05.000Z","updated_at":"2024-07-31T21:20:53.000Z","dependencies_parsed_at":"2024-08-01T01:21:58.390Z","dependency_job_id":null,"html_url":"https://github.com/boobaGreen/investmentAPI","commit_stats":null,"previous_names":["boobagreen/investmentapi"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/boobaGreen/investmentAPI","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/boobaGreen%2FinvestmentAPI","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/boobaGreen%2FinvestmentAPI/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/boobaGreen%2FinvestmentAPI/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/boobaGreen%2FinvestmentAPI/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/boobaGreen","download_url":"https://codeload.github.com/boobaGreen/investmentAPI/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/boobaGreen%2FinvestmentAPI/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31801252,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-14T11:13:53.975Z","status":"ssl_error","status_checked_at":"2026-04-14T11:13:53.299Z","response_time":153,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api","crud","express","jest","jwt","nodejs","prisma","supertest","typescript"],"created_at":"2025-02-14T06:57:27.049Z","updated_at":"2026-04-14T14:31:55.664Z","avatar_url":"https://github.com/boobaGreen.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://img.icons8.com/external-tal-revivo-duo-tal-revivo/100/external-markdown-a-lightweight-markup-language-with-plain-text-formatting-syntax-logo-duo-tal-revivo.png\" width=\"100\" /\u003e\n\u003c/p\u003e\n\u003cp align=\"center\"\u003e\n    \u003ch1 align=\"center\"\u003einvestmentAPI\u003c/h1\u003e\n\u003c/p\u003e\n\u003cp align=\"center\"\u003e\n    \u003cem\u003eClaudio Dall'Ara\u003c/em\u003e\n\u003c/p\u003e\n\u003cp align=\"center\"\u003e\n\t\u003cimg src=\"https://img.shields.io/github/license/boobaGreen/investmentAPI?style=flat\u0026color=0080ff\" alt=\"license\"\u003e\n\t\u003cimg src=\"https://img.shields.io/github/last-commit/boobaGreen/investmentAPI?style=flat\u0026logo=git\u0026logoColor=white\u0026color=0080ff\" alt=\"last-commit\"\u003e\n\t\u003cimg src=\"https://img.shields.io/github/languages/top/boobaGreen/investmentAPI?style=flat\u0026color=0080ff\" alt=\"repo-top-language\"\u003e\n\t\u003cimg src=\"https://img.shields.io/github/languages/count/boobaGreen/investmentAPI?style=flat\u0026color=0080ff\" alt=\"repo-language-count\"\u003e\n\u003cp\u003e\n\u003cp align=\"center\"\u003e\n\t\t\u003cem\u003eDeveloped with the software and tools below.\u003c/em\u003e\n\u003c/p\u003e\n\u003cp align=\"center\"\u003e\n\t\u003cimg src=\"https://img.shields.io/badge/Prettier-F7B93E.svg?style=flat\u0026logo=Prettier\u0026logoColor=black\" alt=\"Prettier\"\u003e\n\t\u003cimg src=\"https://img.shields.io/badge/Jest-C21325.svg?style=flat\u0026logo=Jest\u0026logoColor=white\" alt=\"Jest\"\u003e\n\t\u003cimg src=\"https://img.shields.io/badge/Nodemon-76D04B.svg?style=flat\u0026logo=Nodemon\u0026logoColor=white\" alt=\"Nodemon\"\u003e\n\t\u003cimg src=\"https://img.shields.io/badge/ESLint-4B32C3.svg?style=flat\u0026logo=ESLint\u0026logoColor=white\" alt=\"ESLint\"\u003e\n\t\u003cimg src=\"https://img.shields.io/badge/tsnode-3178C6.svg?style=flat\u0026logo=ts-node\u0026logoColor=white\" alt=\"tsnode\"\u003e\n\t\u003cbr\u003e\n\t\u003cimg src=\"https://img.shields.io/badge/TypeScript-3178C6.svg?style=flat\u0026logo=TypeScript\u0026logoColor=white\" alt=\"TypeScript\"\u003e\n\t\u003cimg src=\"https://img.shields.io/badge/Prisma-2D3748.svg?style=flat\u0026logo=Prisma\u0026logoColor=white\" alt=\"Prisma\"\u003e\n\t\u003cimg src=\"https://img.shields.io/badge/Express-000000.svg?style=flat\u0026logo=Express\u0026logoColor=white\" alt=\"Express\"\u003e\n\t\u003cimg src=\"https://img.shields.io/badge/JSON-000000.svg?style=flat\u0026logo=JSON\u0026logoColor=white\" alt=\"JSON\"\u003e\n\u003c/p\u003e\n\u003chr\u003e\n\n## 🔗 Quick Links\n\n\u003e - [📍 Overview](#-overview)\n\u003e - [📦 Features](#-features)\n\u003e - [📂 Repository Structure](#-repository-structure)\n\u003e - [🚀 Getting Started](#-getting-started)\n\u003e   - [⚙️ Installation](#️-installation)\n\u003e   - [🤖 Running investmentAPI](#-running-investmentAPI)\n\u003e - [📉 DataBase Design](#-database-design)\n\u003e - [🔐 Authentication Flow](#-authentication-flow)\n\u003e - [🔥 API](#-api)\n\u003e - [🧪 Tests](#-tests)\n\u003e - [🛠 Project Roadmap](#-project-roadmap)\n\u003e - [🤝 Contributing](#-contributing)\n\u003e - [📄 License](#-license)\n\u003e - [📧 Contact](#-license)\n\n---\n\n## 📍 Overview\n\n**Exercise Specifications:**\n\nA company has decided to allow frontend-enabling bees to the public\ndevelopers to experiment with those already made bees. Bees will allow you to create and\nread about investments and an API that allows you to see how many and which ones over time\ninvestments have been made. However, before carrying out some operations they must be\nauthorizations via a specific API, which will assign two levels of access based on the request\n\n**Technical specifications:**\n\n1. Create an API that returns a unique, one-time use code\n   which provides access to the rest of the bees with two permission levels, one for\n   reading and one for reading and writing. If the request is made a basic\n   permission to grant that reading and writing otherwise only in\n   reading\n2. Creation of investments\n3. Reading investments\n4. Investment statistics API, which allows you to filter between two periods to see the\n   number or value of investments made in the selected period. Furthermore\n   this api will have to be used for graphs, consequently the values\n   they can be divided by day, week, month and year\n\n**Investment Specifications:** {\ndata creation,\nconfirmation of data,\nvalue,\nannual rate,\nid\n}\n\n**Delivery specifications:**\nThe code must be delivered via a public repository on github, with the\nspecifications to start in code.\nIt is advisable to leave Postman (or similar) files in the repository for API testing\n\n**Implementation Requests:**\n\n- Backend development must be implemented with Node.js and a framework\n  choice(Express favorite)\n- The use of Typescript is mandatory\n- Data can be saved in a SQL db or in a file\n- Carry out e2e tests to verify the correct functioning of the developed APIs\n  (with bookcase of your choice)\n\n**Evaluation methods:**\nThe project will be evaluated based on:\n\n- Code quality and reusability\n- Compliance with requirements\n- Following best practices\n- Correct use of git\n\n---\n\n## 📦 Features\n\n### Investment Management\n\n- **Creation:** Allows the creation of new investments by specifying value and annual interest rate.\n- **Reading:** Enables viewing all investments, a single investment, or aggregate statistics over a defined period.\n- **Update:** Allows modification of an existing investment's details (value, interest rate, confirmation date).\n- **Deletion:** Enables the removal of an investment.\n\n### Authentication\n\n- **JWT Token:** Uses JWT tokens to authenticate users and secure resources. The token is unique and can be used only once.\n- **User Roles:** Supports different user roles : read, readWrite for athl level permission.\n\n### Statistics\n\n- **Data Analysis:** Provides features to analyze investment data, such as performance over time .\n- **Customization:** Allows users to customize statistics according to their needs, including selecting the relevant period and choosing the granularity between day, week, month, and year.\n\n### Other Features\n\n- **Health Check:** Monitors the server's health status.\n- **API Documentation:** Provides detailed documentation of the APIs.\n\n---\n\n## 📂 Repository Structure\n\n```sh\n└── investmentAPI/\n    ├── README.md\n    ├── doc\n    │   ├── authflow\n    │   │   ├── obtain-token-flow.webp\n    │   │   └── using-token-flow.webp\n    │   ├── database\n    │   │   └── databaseInvestmentOnlyImg.webp\n    │   ├── original_track\n    │   │   └── Esercizio_1_be.pdf\n    │   └── test\n    │       └── postman_v2\n    │           ├── InvestmentAPI Local.postman_collection.json\n    │           └── InvestmentAPI Online URL.postman_collection.json\n    ├── jest.config.ts\n    ├── package-lock.json\n    ├── package.json\n    ├── prisma\n    │   ├── dev.db\n    │   ├── schema.prisma\n    │   ├── seed.ts\n    │   ├── seedData\n    │   │   └── investmentSeedData.ts\n    │   ├── test.db\n    │   └── tsconfig.json\n    ├── src\n    │   ├── app.ts\n    │   ├── controllers\n    │   │   ├── errorController.ts\n    │   │   ├── investmentController.ts\n    │   │   └── tokenController.ts\n    │   ├── midllewares\n    │   │   └── authMiddleware.ts\n    │   ├── routes\n    │   │   ├── healthRouter.ts\n    │   │   ├── helpRouter.ts\n    │   │   ├── investmentRouter.ts\n    │   │   └── tokenRouter.ts\n    │   ├── server.ts\n    │   ├── service\n    │   │   ├── investmentService.ts\n    │   │   └── tokenService.ts\n    │   ├── test\n    │   │   ├── healthRouter\n    │   │   │   └── health.test.ts\n    │   │   ├── investmentRouter\n    │   │   │   ├── createInvestment.test.ts\n    │   │   │   ├── deleteInvestment.test.ts\n    │   │   │   ├── getOneInvestment.test.ts\n    │   │   │   ├── getallInvestments.test.ts\n    │   │   │   ├── statsInvestment.test.ts\n    │   │   │   └── updateInvestment.test.ts\n    │   │   ├── setupTest.ts\n    │   │   ├── tokenRouter\n    │   │   │   └── tokenRouter.test.ts\n    │   │   └── utils\n    │   │       └── deleteExpiredTokens.test.ts\n    │   ├── types\n    │   │   ├── TInvestment.ts\n    │   │   └── TUser.ts\n    │   └── utils\n    │       ├── appError.ts\n    │       ├── catchAsync.ts\n    │       ├── cleanupService.ts\n    │       ├── cookieUtils.ts\n    │       ├── dateUtils.ts\n    │       ├── dbServer.ts\n    │       ├── jwtConfig.ts\n    │       └── logger.ts\n    └── tsconfig.json\n```\n\n---\n\n## 🚀 Getting Started\n\n**_Requirements_**\n\nEnsure you have the following dependencies installed on your system:\n\n- **TypeScript**: `version x.y.z`\n\n### ⚙️ Installation\n\n### Prerequisites\n\nYou need to have Node.js (\u003e= 22.2.0) installed locally .\nUseful links:\n\n- Node.js: https://nodejs.org/en/download/package-manager\n\n### Main Installation\n\n1. Clone the investmentAPI repository:\n\n```sh\ngit clone https://github.com/boobaGreen/investmentAPI\n```\n\n2. Change to the project directory:\n\n```sh\ncd investmentAPI\n```\n\n3. Important: For the project to function properly, both environment files for development and testing are required.\n   Rename the files .env.example and env.test.example to .env and .env.test, respectively, and adjust the variables contained within as needed. You can leave them as they are for an initial test.\n\n4. Install the dependencies:\n\n```sh\nnpm install\n```\n\n5. Create the .env files. For the project, we plan to create 2 databases, one for 'dev' and one for 'test'. To keep the two environments separate, I have prepared scripts to facilitate their creation and initial seeding.\n   he main environment file is called .env, while the one for testing is .env.test. I have provided two example files for reference: .env.example and .env.test.example\n\n6. Now let's create the 2 databases with this commands :\n\n```sh\nnpm run dbdev:push\n```\n\n```sh\nnpm run dbtest:push\n```\n\n7. Populate them with sample records if necessary using seed data :\n\n```sh\nnpm run dbdev:seed\n```\n\n```sh\nnpm run dbtest:seed\n```\n\n### 🤖 Running investmentAPI\n\n#### Local Run\n\nUse the following command to run investmentAPI with nodeman:\n\n```sh\nnpm run dev\n```\n\n#### Deploy - for Production\n\nUse the following command to build:\n\n```sh\nnpm run build\n```\n\nUse the following command to run:\n\n```sh\nnpm start\n```\n\n---\n\n## 📉 DataBase Design\n\nThe project includes three tables in the database: User, Token, and Investment. The details of these tables are illustrated in the image below.\n\n![Database Design](./doc/database/databaseInvestmentOnlyImg.webp)\n\n---\n\n## 🔐 Authentication Flow\n\nThe application supports two levels of authorization:\n\n1. **JWT Token Generation**\n\n   - **Endpoint**: POST `/api/token`\n   - **Credentials**: Basic authentication (username and password)\n   - **Response**:\n     - A JWT with an expiration time\n       - Configurable via the environment variable `JWT_HOUR_EXPIRATION`\n       - Expiration time in hours (minimum 1 hour, maximum 24 hours, only integer values are allowed)\n       - Defaults to 1 hour for invalid formats and 24 hours if the value exceeds the maximum limit\n\n2. **Token Types**\n\n   - **Read-Write Token**\n     - Obtained with correct credentials (username and password)\n   - **Read-Only Token**\n     - Issued when the POST request body is empty\n\n3. **Error Handling**\n\n   - **Incorrect or Partial Credentials**: An error is returned\n\n4. **Token Requirements**\n\n   - Tokens must be unique and valid for a single use\n   - Ensured by:\n     - Generating the JWT with a secret key\n     - Including a unique username and creation date in the payload\n\n5. **Single-Use Token Enforcement**\n   - Upon token issuance:\n     - The token is sent back as a JWT cookie\n     - Recorded in a database with a `used` field set to `false`\n   - Upon token usage:\n     - The `used` field is flagged as `true`\n     - Subsequent requests with the same JWT will result in an error\n\nFor a visual representation of the authentication flow, refer to the diagram below-\n\n![Authentication Flow-obtain-token](./doc/authflow/obtain-token-flow.webp)\n![Authentication Flow-using-token](./doc/authflow/using-token-flow.webp)\n\n### 🗑️ Token Cleanup\n\nTo manage expired JWTs efficiently, a cleanup function utilizing cron jobs is implemented. This function periodically executes to:\n\n- **Check Expiration**: Verify which JWTs recorded in the `Token` table have expired based on their expiration time.\n- **Remove Invalid Tokens**: Delete tokens that are no longer valid due to expiration, enhancing scalability and maintaining a clean dataset.\n\nInitially, the cron job was set to run hourly. However, for optimization, it has been configured to execute based on the `JWT_HOUR_EXPIRATION` environment variable, aligning the cleanup frequency with the JWT expiration settings.\n\n## 🔥 API\n\n### Health Check\n\n- **Method:** GET\n- **Path:** `/`\n- **Description:** Provides a basic health check to confirm that the server is operational. This endpoint returns a JSON object indicating the server's status and ensures that the server is up and running correctly.\n- **Authentication:** Not required\n- **Response:**\n  - **Status:** 200 OK\n  - **Body:**\n    ```json\n    {\n      \"status\": \"success\",\n      \"message\": \"Server is healthy\"\n    }\n    ```\n  - **Fields:**\n    - `status`: Indicates the success status of the health check.\n    - `message`: A message confirming that the server is operational.\n- **Notes:**\n  - This endpoint is useful for monitoring and checking the server's availability.\n  - It does not require any request parameters or authentication.\n\n### API Documentation\n\n- **Method:** GET\n- **Path:** `/api`\n- **Description:** Provides a comprehensive list of all available API endpoints and their descriptions. This endpoint serves as a reference for developers to understand how to interact with the API.\n- **Authentication:** Not required\n- **Response:**\n  - **Status:** 200 OK\n  - **Body:**\n    ```json\n    {\n      \"status\": \"success\",\n      \"message\": \"List of all available API endpoints\",\n      \"endpoints\": [\n        {\n          \"method\": \"GET\",\n          \"path\": \"/api\",\n          \"description\": \"Provides a list of all available API endpoints and their descriptions.\"\n        },\n        {\n          \"method\": \"GET\",\n          \"path\": \"/api/health\",\n          \"description\": \"Health check endpoint to verify that the server is operational.\"\n        },\n        {\n          \"method\": \"POST\",\n          \"path\": \"/api/token\",\n          \"description\": \"Generates a JWT token. Requires a POST request with \\\"username\\\" and \\\"password\\\". On success, returns an HTTP-only JWT cookie with \\\"readWrite\\\" permissions. If the credentials are incorrect or missing, a generic 400 error is returned. If the payload is empty, a single-use JWT token with \\\"read\\\" permissions is issued.\"\n        },\n        {\n          \"method\": \"POST\",\n          \"path\": \"/api/investment\",\n          \"description\": \"Creates a new investment record. Requires a single-use JWT token with \\\"readWrite\\\" permissions in the cookie. The request body must include \\\"value\\\" (integer) and \\\"annualInterestRate\\\" (float). If required fields are missing or the JWT token lacks appropriate permissions, a generic 400 error will be returned. On successful creation, a 201 status is returned along with the newly created investment object. If the token has only \\\"read\\\" permissions, a 400 error is returned, and if required fields are missing from the request body, a 400 error is also returned.\"\n        },\n        {\n          \"method\": \"GET\",\n          \"path\": \"/api/investment\",\n          \"description\": \"Retrieves a list of all investments. Requires a JWT token (single-use) in the cookie with \\\"read\\\" or \\\"readWrite\\\" permissions. Returns a detailed list of investments including value, annual interest rate, creation date, and confirmation status.\"\n        },\n        {\n          \"method\": \"GET\",\n          \"path\": \"/api/investment/stats\",\n          \"description\": \"Fetches investment statistics based on a specified date range and granularity. Requires a JWT token (single-use) in the cookie with \\\"read\\\", \\\"readWrite\\\", or \\\"read\\\" permissions. Query parameters must include \\\"startDate\\\" (YYYY-MM-DD), \\\"endDate\\\" (YYYY-MM-DD), and \\\"granularity\\\" (one of \\\"day\\\", \\\"week\\\", \\\"month\\\", \\\"year\\\"). If any required parameter is missing or if the token does not have sufficient permissions, a generic 400 error is returned. On successful retrieval, a 200 status code is returned with the investment statistics.\"\n        },\n        {\n          \"method\": \"PATCH\",\n          \"path\": \"/api/investment\",\n          \"description\": \"Updates an existing investment record. Requires a single-use JWT token with \\\"readWrite\\\" permissions in the cookie. The request body can include the following fields: \\\"value\\\" (integer), \\\"annualInterestRate\\\" (float), and \\\"confirmedAt\\\" (ISO 8601 date format). The \\\"confirmedAt\\\" date must be greater than the creation date of the investment. If any field is invalid or missing, or if the JWT token lacks appropriate permissions, a generic 400 error is returned. On successful update, a 200 status code is returned with the updated investment object.\"\n        }\n      ]\n    }\n    ```\n  - **Fields:**\n    - `status`: Indicates the success status of the request.\n    - `message`: A message providing context about the response.\n    - `endpoints`: An array of objects, each describing an available API endpoint, including:\n      - `method`: HTTP method used for the endpoint.\n      - `path`: The endpoint path.\n      - `description`: A brief description of the endpoint's purpose and requirements.\n- **Notes:**\n  - This endpoint serves as a reference guide for all available API routes and their functionalities.\n  - It does not require authentication and provides a helpful overview of the API structure.\n\n### Get Authorization Token\n\n- **Method:** POST\n- **Path:** `/api/token/`\n- **Description:** Generates a JWT token. If a request with correct Basic Auth credentials (username and password) is provided, a single-use JWT token with read and write permissions is issued. The token's maximum validity is determined by the `JWT_HOUR_EXPIRATION` environment variable set in the `.env` file. If the request contains incorrect or missing fields, a generic 400 error is returned. If no credentials are provided in the request payload, a single-use JWT token with read-only permissions and a specific expiration time will be issued.\n- **Authentication:**\n  - **Basic Auth**: Requires username and password for a read-write token.\n  - **No Auth**: Issues a read-only token if no credentials are provided.\n- **Response:**\n  - **Successful**: A JWT Cookie token is returned with appropriate permissions.\n  - **Error**: A generic 400 error is returned for invalid or missing credentials or fields.\n- **Notes:**\n  - A detailed flowchart of the authentication logic can be found in the `/doc/authflow/auth-flowchart.pdf` file.\n\n### Get All Investments\n\n- **Method:** GET\n- **Path:** `/api/investments/`\n- **Description:** Retrieves a list of all investment records in the system.\n- **Authentication:** Required (Valid JWT with `read` or `readWrite` permissions)\n- **Response:**\n  - **Status:** 200 OK\n  - **Body:**\n    ```json\n    {\n      \"status\": \"success\",\n      \"doc\": [\n        {\n          \"id\": \u003cinvestment_id\u003e,\n          \"value\": \u003cinvestment_value\u003e,\n          \"annualInterestRate\": \u003cinterest_rate\u003e,\n          \"createdAt\": \"\u003ccreation_date\u003e\",\n          \"confirmedAt\": \"\u003cconfirmation_date_or_null\u003e\"\n        },\n        ...\n      ]\n    }\n    ```\n  - **Fields:**\n    - `status`: Indicates the success status of the request.\n    - `doc`: An array of investment objects. Each object contains:\n      - `id`: Unique identifier for the investment.\n      - `value`: The value of the investment.\n      - `annualInterestRate`: The annual interest rate of the investment.\n      - `createdAt`: The date and time when the investment was created.\n      - `confirmedAt`: The date and time when the investment was confirmed (or `null` if not confirmed).\n\n### Get Investment by ID\n\n- **Method:** GET\n- **Path:** `/api/investments/:id`\n- **Description:** Retrieves a specific investment record based on the provided investment ID.\n- **Parameters:**\n  - `id` (path parameter): The unique identifier of the investment to retrieve.\n- **Authentication:** Required (Valid JWT with `read` or `readWrite` permissions)\n- **Response:**\n  - **Status:** 200 OK\n  - **Body:**\n    ```json\n    {\n      \"status\": \"success\",\n      \"data\": {\n        \"id\": \u003cinvestment_id\u003e,\n        \"createdAt\": \"\u003ccreation_date\u003e\",\n        \"confirmedAt\": \"\u003cconfirmation_date_or_null\u003e\",\n        \"value\": \u003cinvestment_value\u003e,\n        \"annualInterestRate\": \u003cinterest_rate\u003e\n      }\n    }\n    ```\n  - **Fields:**\n    - `status`: Indicates the success status of the request.\n    - `data`: Contains the details of the requested investment:\n      - `id`: The unique identifier for the investment.\n      - `createdAt`: The date and time when the investment was created.\n      - `confirmedAt`: The date and time when the investment was confirmed (or `null` if not confirmed).\n      - `value`: The value of the investment.\n      - `annualInterestRate`: The annual interest rate of the investment.\n\n#### Create New Investment\n\n- **Method:** POST\n- **Path:** `/api/investments/`\n- **Description:** Creates a new investment record with the specified details.\n- **Authentication:** Required (Valid JWT token with `readWrite` permissions)\n- **Request Body:**\n  - `value` (required): The value of the investment. Should be a decimal number representing the investment amount.\n  - `annualRate` (required): The annual interest rate of the investment. Should be a decimal number.\n  - `createdAt` (optional): The date and time when the investment was created, in ISO date format (e.g., `2024-07-25T11:30:07.454Z`). If not provided, the current date and time will be used.\n  - `confirmDate` (optional): The date and time when the investment was confirmed, in ISO date format (e.g., `2024-07-25T11:30:07.454Z`). This date must be later than or equal to `createdAt`. If not provided, the field will be set to `null`.\n- **Response:**\n  - **Status:** 201 Created\n  - **Body:**\n    ```json\n    {\n      \"status\": \"success\",\n      \"data\": {\n        \"id\": \u003cinvestment_id\u003e,\n        \"createdAt\": \"\u003ccreation_date\u003e\",\n        \"confirmedAt\": \"\u003cconfirmation_date_or_null\u003e\",\n        \"value\": \u003cinvestment_value\u003e,\n        \"annualInterestRate\": \u003cannual_rate\u003e\n      }\n    }\n    ```\n  - **Fields:**\n    - `status`: Indicates the success status of the request.\n    - `data`: Contains the details of the newly created investment:\n      - `id`: The unique identifier for the new investment.\n      - `createdAt`: The date and time when the investment was created.\n      - `confirmedAt`: The date and time when the investment was confirmed, or `null` if it has not been confirmed yet.\n      - `value`: The value of the investment.\n      - `annualInterestRate`: The annual interest rate of the investment.\n\n### Update Investment\n\n- **Method:** PATCH\n- **Path:** `/api/investments/:id`\n- **Description:** Updates the confirmation date of an existing investment.\n- **Authentication:** Required (Valid JWT token with `readWrite` permissions)\n- **Request Body:**\n\n  - `value` (optional): The new vaaìlue of investment . Must be an integer.\n  - `annualInterestRate` (optional): The new value of annualInterestRate. Must be a float.\n  - `confirmDate` (optional): The new confirmation date in ISO date format (e.g., `2024-01-01T00:00:00.000Z`).\n    Partial date formats are also accepted, such as 2024-07-05. These will be completed by adding zeroes to the missing parts and converted to a compatible format.\n    This date must not be earlier than the investment's creation date (`createdAt`).\n\n- **Response:**\n  - **Status:** 200 OK\n  - **Body:**\n    ```json\n    {\n      \"status\": \"success\",\n      \"data\": {\n        \"id\": \u003cinvestment_id\u003e,\n        \"createdAt\": \"\u003ccreation_date\u003e\",\n        \"confirmedAt\": \"\u003cnew_confirmation_date\u003e\",\n        \"value\": \u003cinvestment_value\u003e,\n        \"annualInterestRate\": \u003cannual_rate\u003e\n      }\n    }\n    ```\n  - **Fields:**\n    - `status`: Indicates the success status of the request.\n    - `data`: Contains the details of the updated investment:\n      - `id`: The unique identifier of the investment.\n      - `createdAt`: The original creation date of the investment.\n      - `confirmedAt`: The updated confirmation date of the investment.\n      - `value`: The value of the investment.\n      - `annualInterestRate`: The annual interest rate of the investment.\n\n### Get Investment Statistics\n\n- **Method:** GET\n- **Path:** `/api/investments/stats`\n- **Description:** Retrieves investment statistics for a specified time range and granularity. The data includes total investments and their aggregate value, broken down by the specified grouping period (day, week, month, or year).\n- **Query Parameters:**\n  - `startDate` (required): Start date of the range in ISO date format (e.g., 2023-01-01). Partial dates (e.g., 2024-07) will be completed to the full format (2024-07-01).\n  - `endDate` (required): End date of the range in ISO date format (e.g., 2023-12-31). Partial dates will be completed similarly.\n  - `granularity` (required): The period for grouping data. Accepted values are \"day\", \"week\", \"month\", or \"year\".\n- **Authentication:** Required (Valid JWT with `read` or `readWrite` permissions)\n- **Response:**\n\n  - **Status:** 200 OK\n  - **Body:**\n    `json\n{\n  \"status\": \"success\",\n  \"data\": {\n    \"totalInvestments\": \u003ctotal_investments\u003e,\n    \"totalValue\": \u003ctotal_value\u003e,\n     \"details\": {\n      \"\u003cperiod\u003e\": {\n        \"count\": \u003cnumber_of_investments\u003e,\n        \"totalValue\": \u003ctotal_value_for_period\u003e\n      }\n    }\n  }\n}\n`\n  - **Fields:**\n\n    - `status: Indicates the success status of the request.\n    - `data`: Contains the statistics for the specified date range:\n      - `totalInvestments`: Total number of investments within the specified date range.\n      - `totalValue`: Aggregate value of all investments within the specified date range.\n      - `details`: An object where keys are the periods (e.g., years, months, weeks) and values are objects containing:\n        - `count`: Number of investments in that period.\n        - `totalValue`:Total value of investments in that period.\n\n---\n\n## 🧪 Tests\n\n### Jest e SuperTest:\n\nThe project includes a comprehensive suite of automated tests utilizing Jest and Supertest. These tests cover various aspects of the application, including end-to-end (E2E) scenarios and critical sections of the codebase.\n\n- **Unit Tests**: Ensures individual components and functions work as expected.\n- **Integration Tests**: Verifies that different parts of the application work together correctly.\n- **End-to-End (E2E) Tests**: Simulates real user scenarios to test the application in a production-like environment.\n\nAll major functionalities and code paths have been covered by these tests. However, additional tests can be added to cover more specific cases or edge scenarios as needed.\n\nTo run the tests, use the following commands:\n\n- `npm test` - for running all the unit and integration tests.\n\n```sh\nnpm run test\n```\n\n### Postman\n\nThe collections are also available for direct download in the /doc/postman_v2 folder, or you can use the corresponding buttons to access them online.\n\nBefore accessing any endpoint that requires read or write authentication, you need to make a request to the /token endpoint to obtain a valid single-use token.\n\n#### POSTMAN COLLECTION ON LINE (Render.com)\n\nThis collection of postman points to the service deployed online on render.com. so the base URL is: https://investmentapi.onrender.com/\n\nBetween the local and the online-ready versions, you could also use a URL tied to an environment variable, but for simplicity, I have prepared two dedicated collections:\n\n[\u003cimg src=\"https://run.pstmn.io/button.svg\" alt=\"Run In Postman\" style=\"width: 128px; height: 32px;\"\u003e](https://god.gw.postman.com/run-collection/37112030-591524f7-2678-46c1-be3e-806034a0afd0?action=collection%2Ffork\u0026source=rip_markdown\u0026collection-url=entityId%3D37112030-591524f7-2678-46c1-be3e-806034a0afd0%26entityType%3Dcollection%26workspaceId%3D4ff310f0-17de-4e59-a39d-b71459c423ec)\n\n#### POSTMAN COLLECTION FOR LOCALHOST\n\nThis collection of postman points to the possible service deployed locally on port 8000 for example, so the base URL is: http://localhost:8000\n\nBetween the local and the online-ready versions, you could also use a URL tied to an environment variable, but for simplicity, I have prepared two dedicated collections:\n\n[\u003cimg src=\"https://run.pstmn.io/button.svg\" alt=\"Run In Postman\" style=\"width: 128px; height: 32px;\"\u003e](https://god.gw.postman.com/run-collection/37112030-38e21a9b-fe21-40f3-8273-247081bf6b9b?action=collection%2Ffork\u0026source=rip_markdown\u0026collection-url=entityId%3D37112030-38e21a9b-fe21-40f3-8273-247081bf6b9b%26entityType%3Dcollection%26workspaceId%3D4ff310f0-17de-4e59-a39d-b71459c423ec)\n\n### Insomnia\n\nIf you prefer to use Insomnia, you can import the Postman files either locally or online, as they are compatible.\n\n---\n\n## 🛠 Project Roadmap\n\n- [ ] `► Add the \"OnlyConfimed\" field to all the GET investment or STAT request method to filter OUT the NOT confirmed investments `\n\n---\n\n## 🤝 Contributing\n\nContributions are welcome! Here are several ways you can contribute:\n\n- **[Submit Pull Requests](https://github.com/boobaGreen/investmentAPI/blob/main/CONTRIBUTING.md)**: Review open PRs, and submit your own PRs.\n- **[Join the Discussions](https://github.com/boobaGreen/investmentAPI/discussions)**: Share your insights, provide feedback, or ask questions.\n- **[Report Issues](https://github.com/boobaGreen/investmentAPI/issues)**: Submit bugs found or log feature requests for Investmentapi.\n\n\u003cdetails closed\u003e\n    \u003csummary\u003eContributing Guidelines\u003c/summary\u003e\n\n1. **Fork the Repository**: Start by forking the project repository to your GitHub account.\n2. **Clone Locally**: Clone the forked repository to your local machine using a Git client.\n   ```sh\n   git clone https://github.com/boobaGreen/investmentAPI\n   ```\n3. **Create a New Branch**: Always work on a new branch, giving it a descriptive name.\n   ```sh\n   git checkout -b new-feature-x\n   ```\n4. **Make Your Changes**: Develop and test your changes locally.\n5. **Commit Your Changes**: Commit with a clear message describing your updates.\n   ```sh\n   git commit -m 'Implemented new feature x.'\n   ```\n6. **Push to GitHub**: Push the changes to your forked repository.\n   ```sh\n   git push origin new-feature-x\n   ```\n7. **Submit a Pull Request**: Create a PR against the original project repository. Clearly describe the changes and their motivations.\n\nOnce your PR is reviewed and approved, it will be merged into the main branch.\n\n\u003c/details\u003e\n\n---\n\n## 📃 License\n\n[MIT](https://choosealicense.com/licenses/mit/)\n\n---\n\n## 📧 Contact\n\nAny questions? Send me an e-mail here: [claudiodallaradev@gmail.com](claudiodallaradev@gmail.com)  \nYou can find my Linkedin profile here: [https://www.linkedin.com/in/claudio-dall-ara-730aa0302/](https://www.linkedin.com/in/claudio-dall-ara-730aa0302/)\n\n---\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fboobagreen%2Finvestmentapi","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fboobagreen%2Finvestmentapi","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fboobagreen%2Finvestmentapi/lists"}