diff --git a/.env.sample b/.env.sample index 7bdf0cdf99..a4ebd50a0d 100644 --- a/.env.sample +++ b/.env.sample @@ -91,3 +91,4 @@ LOG_LEVEL = info REDIS_HOST= REDIS_PORT= +REDIS_PASSWORD= \ No newline at end of file diff --git a/INSTALLATION.md b/INSTALLATION.md index fb79a6cc86..059a96df2f 100644 --- a/INSTALLATION.md +++ b/INSTALLATION.md @@ -16,30 +16,30 @@ This document provides instructions on how to set up and start a running instanc - [Install MongoDB](#install-mongodb) - [Setting up the mongoDB database](#setting-up-the-mongodb-database) - [Install Redis](#install-redis) - - [Setting Up Redis](#setting-up-redis ) + - [Setting Up Redis](#setting-up-redis) - [Benchmark For Performance Benefits](#performance-benefits) - [Configuration](#configuration) - [The .env Configuration File](#the-env-configuration-file) - [Generating Token Secrets](#generating-token-secrets) - - [Setting up ACCESS\_TOKEN\_SECRET in .env file](#setting-up-access_token_secret-in-env-file) + - [Setting up ACCESS_TOKEN_SECRET in .env file](#setting-up-access_token_secret-in-env-file) - [Linux](#linux) - [Windows](#windows) - - [Setting up REFRESH\_TOKEN\_SECRET in .env file](#setting-up-refresh_token_secret-in-env-file) + - [Setting up REFRESH_TOKEN_SECRET in .env file](#setting-up-refresh_token_secret-in-env-file) - [Configuring MongoDB](#configuring-mongodb) - - [Setting up the MONGODB\_URL in .env file](#setting-up-the-mongodb_url-in-env-file) - - [Using the CLI to get the MONGODB\_URL Connection String](#using-the-cli-to-get-the-mongodb_url-connection-string) - - [Using Microsoft Windows to get the MONGODB\_URL Connection String](#using-microsoft-windows-to-get-the-mongodb_url-connection-string) + - [Setting up the MONGODB_URL in .env file](#setting-up-the-mongodb_url-in-env-file) + - [Using the CLI to get the MONGODB_URL Connection String](#using-the-cli-to-get-the-mongodb_url-connection-string) + - [Using Microsoft Windows to get the MONGODB_URL Connection String](#using-microsoft-windows-to-get-the-mongodb_url-connection-string) - [Configuring Redis](#configuring-redis) - [For Local Setup (Linux and WSL)](#for-local-setup-linux-and-wsl) - [For Remote Setup (Redis Cloud)](#for-remote-setup-redis-cloud) - - [Setting up .env LAST\_RESORT\_SUPERADMIN\_EMAIL parameter](#setting-up-env-last_resort_superadmin_email-parameter) + - [Setting up .env LAST_RESORT_SUPERADMIN_EMAIL parameter](#setting-up-env-last_resort_superadmin_email-parameter) - [Configuring Google ReCAPTCHA](#configuring-google-recaptcha) - - [Setting up RECAPTCHA\_SECRET\_KEY in .env file](#setting-up-recaptcha_secret_key-in-env-file) - - [Setting up .env MAIL\_USERNAME and MAIL\_PASSWORD ReCAPTCHA Parameters](#setting-up-env-mail_username-and-mail_password-recaptcha-parameters) + - [Setting up RECAPTCHA_SECRET_KEY in .env file](#setting-up-recaptcha_secret_key-in-env-file) + - [Setting up .env MAIL_USERNAME and MAIL_PASSWORD ReCAPTCHA Parameters](#setting-up-env-mail_username-and-mail_password-recaptcha-parameters) - [Setting up .env SMTP Variables](#setting-up-env-smtp-variables) - - [Setting up Logger configurations *(optional)*](#setting-up-logger-configurations-optional) - - [Setting up COLORIZE\_LOGS in .env file](#setting-up-colorize_logs-in-env-file) - - [Setting up LOG\_LEVEL in .env file](#setting-up-log_level-in-env-file) + - [Setting up Logger configurations _(optional)_](#setting-up-logger-configurations-optional) + - [Setting up COLORIZE_LOGS in .env file](#setting-up-colorize_logs-in-env-file) + - [Setting up LOG_LEVEL in .env file](#setting-up-log_level-in-env-file) - [Configuring Google Firebase](#configuring-google-firebase) - [Generate Firebase Keys for the Talawa Notification Service](#generate-firebase-keys-for-the-talawa-notification-service) - [(Mobile Developers Only) Applying the Firebase Keys to the Talawa Mobile App](#mobile-developers-only-applying-the-firebase-keys-to-the-talawa-mobile-app) @@ -68,6 +68,7 @@ This document provides instructions on how to set up and start a running instanc - [Testing](#testing) + # Installation You will need to have copies of your code on your local system. Here's how to do that. @@ -87,10 +88,11 @@ First you need a local copy of talawa-api. Run the following command in the dire 1. Navigate to the folder where you want to setup the repository. Here, I will set it up in a folder called `talawa`. 1. Navigate to the folder and open a terminal in this folder (you can right-click and choose appropiate option based onn your OS). Next, we'll fork and clone the `talawa-api` repository. 1. Navigate to [https://github.com/PalisadoesFoundation/talawa-api/](hhttps://github.com/PalisadoesFoundation/talawa-api/) and click on the `fork` button. It is placed on the right corner opposite the repository name `PalisadoesFoundation/talawa-api`. -![Image with fork](./image/install1.png) + ![Image with fork](./image/install1.png) 1. You should now see `talawa-api` under your repositories. It will be marked as forked from `PalisadoesFoundation/talawa-api` -![Image of user's clone](./image/install2.png) + ![Image of user's clone](./image/install2.png) 1. Clone the repository to your local computer (replacing the values in `{{}}`): + ``` $ git clone https://github.com/{{YOUR GITHUB USERNAME}}/talawa-api.git ``` @@ -102,6 +104,7 @@ This will setup the repository and the code files locally for you. For more deta ## Install the Required Packages Install the packages required by `talawa-api` using this command: + ``` npm install ``` @@ -118,17 +121,16 @@ We're listing some common approaches to set up a running instance of MongoDB dat 2. `Hosted database approach:` MongoDB Atlas is the easiest way to get a running instance of mongodb database. It is a hosted(remote) mongodb database provided by mongodb itself. If you're a beginner and don't want too much of a hassle setting up the database you should use this approach but you should eventually switch to local instance. Follow the setup guide on official [MongoDB Atlas Docs](https://www.mongodb.com/docs/atlas/getting-started/). Mongodb Atlas is just one of the many hosted database solutions. Some issues that you might face while using this are slower tests, slower API requests, dependence on Internet connection etc. 3. `Docker container approach:` If you are fluent in working with docker you should use this approach. Docker is a great way to manage and run applications without natively installing anything on your system. With this you can set up the mongodb database inside a docker container and manage it as per your will. Follow this [video tutorial](https://www.youtube.com/watch?v=uklyCSKQ1Po) to set up a mongodb docker container. You can learn about docker from [Docker docs](https://docs.docker.com/). -## Install Redis +## Install Redis Talawa-api makes use of `Redis` for caching frequently accessed data items in the primary database. We make use of `ioredis` to interact with the `redis-server` from within the code. The main Idea is the in production this will act as an in-memory cache. So it is recommended that you set it up locally. However for simplicity purposes, a section to accomodate for setting Redis up using a remote instance like Redis Cloud has been added. Please note that this is not recommended since the remote connection takes a considerable amount of time to be considered as a cache to improve application performance. -### Performance Benefits +### Performance Benefits + ![Screenshot from 2023-08-26 18-37-34](https://github.com/kb-0311/talawa-api/assets/96020697/e8b99d5c-6abf-4e71-999c-f8ae1e84de45) ![Screenshot from 2023-08-26 18-37-48](https://github.com/kb-0311/talawa-api/assets/96020697/55d1388d-cc15-4d5e-931d-6befa0fa7a10) - -### Setting Up Redis - +### Setting Up Redis 1. `For Linux Users`: @@ -137,16 +139,19 @@ Talawa-api makes use of `Redis` for caching frequently accessed data items in th - **Step 1**: Open a terminal. - **Step 2**: Update the package list: + ```bash sudo apt update ``` - **Step 3**: Install Redis Server: + ```bash sudo apt install redis-server ``` - **Step 4**: Start the Redis service: + ```bash sudo service redis-server start ``` @@ -160,29 +165,33 @@ Talawa-api makes use of `Redis` for caching frequently accessed data items in th If you'd rather not deal with the hassle of setting up WSL on your computer, there's another option: you can use a hosted database like Redis Cloud. More details about this are provided below, mainly for when you're working on development tasks. But it's a good idea to set up Redis on your own computer if you can. Right now, Redis isn't supported directly on Windows – you can only install and use it through WSL. If you're a Windows user and want to get Redis working using the Windows Subsystem for Linux (WSL), just follow these steps: - - **Step 1**: Install WSL (Windows Subsystem for Linux) following the official [WSL Installation Guide](https://docs.microsoft.com/en-us/windows/wsl/install). +- **Step 1**: Install WSL (Windows Subsystem for Linux) following the official [WSL Installation Guide](https://docs.microsoft.com/en-us/windows/wsl/install). - - **Step 2**: Open a WSL terminal. +- **Step 2**: Open a WSL terminal. - - **Step 3**: Update the package list: - ```bash - sudo apt update - ``` +- **Step 3**: Update the package list: - - **Step 4**: Install Redis Server: - ```bash - sudo apt install redis-server - ``` + ```bash + sudo apt update + ``` - - **Step 5**: Start the Redis service: - ```bash - sudo service redis-server start - ``` +- **Step 4**: Install Redis Server: + + ```bash + sudo apt install redis-server + ``` + +- **Step 5**: Start the Redis service: + + ```bash + sudo service redis-server start + ``` + +- **Step 6**: Test if Redis is running by running the Redis CLI: + ```bash + redis-cli + ``` - - **Step 6**: Test if Redis is running by running the Redis CLI: - ```bash - redis-cli - ``` 3. **Connecting to Redis Cloud**: To connect to a Redis cloud service, you will need the host and port information provided by your cloud service provider. Use these values in your application to establish a connection. Typically, the host and port strings are provided in the following format: @@ -190,7 +199,7 @@ If you'd rather not deal with the hassle of setting up WSL on your computer, the - Host: `your-redis-host.redisprovider.com` - Port: `6379` (default Redis port) - Replace `your-redis-host.redisprovider.com` with the actual host provided by your Redis cloud service. You can then use these values in your application's configuration to connect to your Redis cloud instance. + Replace `your-redis-host.redisprovider.com` with the actual host provided by your Redis cloud service. You can then use these values in your application's configuration to connect to your Redis cloud instance. You may also have to enter Redis Password and Username for using cloud instance. Remember to adjust any paths or details as needed for your specific environment. After following these steps, you will have successfully set up Redis. @@ -204,9 +213,11 @@ npm run setup ``` It can be done manually as well and here's how to do it. + ## The .env Configuration File A file named `.env` is required in the root directory of talawa-api for storing environment variables used at runtime. It is not a part of the repo and you will have to create it. For a sample of `.env` file there is a file named `.env.sample` in the root directory. Create a new `.env` file by copying the contents of the `.env.sample` into `.env` file. + ``` cp .env.sample .env ``` @@ -227,7 +238,7 @@ This `.env` file must be populated with the following environment variables for | REDIS HOST | Used for connecting talawa-api to the redis instance | | REDIS_PORT | Specifies the port of the active redis-server | | REDIS_PASSWORD(optional) | Used for authenticating the connection request to | -| | a hosted redis-server | +| | a hosted redis-server | The following sections will show you how to configure each of these parameters. @@ -240,12 +251,15 @@ Access and refresh token secrets are used for authentication purposes. Run the following command and copy/paste the result to the variable named `ACCESS_TOKEN_SECRET` in `.env` file. #### Linux + The command to use is: + ``` openssl rand -hex 32 ``` + #### Windows - + This command is available if you have [Git for Windows](https://gitforwindows.org/) installed. Follow these steps: 1. Install `Git for Windows` @@ -265,19 +279,23 @@ openssl rand -hex 32 ``` ## Configuring MongoDB + Here's how you will configure MongoDB. **NOTE**: Talawa-API uses **2** databases, a primary and test version. + 1. You only have to setup one database and provide it's URL in the `.env` file. This is the`primary database` and is used to store all your data. 1. We automatically create a new database with the name `TALAWA_API_TEST_DATABASE`. This is exclusively used for storing all the test data generated during the testing process so that it does not bloat the main database with unnecessary data. ### Setting up the MONGODB_URL in .env file -A `Connection String` is the URL that applications use to access a MongoDB database. Talawa-API will need to know the correct connection string to use to perform correctly. + +A `Connection String` is the URL that applications use to access a MongoDB database. Talawa-API will need to know the correct connection string to use to perform correctly. 1. The `Connection String` is the `.env` variable named `MONGO_DB_URL` in the `.env` file. -1. The `Connection String` can differ depending on the approach you used to set up your database instance. Please read the official [mongodb docs](https://www.mongodb.com/docs/manual/reference/connection-string/) on `connection string`. +1. The `Connection String` can differ depending on the approach you used to set up your database instance. Please read the official [mongodb docs](https://www.mongodb.com/docs/manual/reference/connection-string/) on `connection string`. ### Using the CLI to get the MONGODB_URL Connection String + Your MongoDB installation may include either the `mongo` or `mongosh` command line utility. An easy way of determining the `connection string` is to: 1. Run the command line utility @@ -300,17 +318,20 @@ For mongosh info see: https://docs.mongodb.com/mongodb-shell/ ... ``` + ### Using Microsoft Windows to get the MONGODB_URL Connection String + There are a few more steps that need to be done in a Windows environment. 1. Download the MongoDB Shell from the tools section at the following link:[Mongo Shell](https://www.mongodb.com/try/download/shell) 1. Extract the downloaded shell folder, locate the `mongosh` application, and paste it to the following location: `Program Files` -> `MongoDB` -> `bin`. - 1. You will find the mongosh application inside the `bin` folder] + 1. You will find the mongosh application inside the `bin` folder] 1. Add the path of the location where you pasted the `mongosh` application to your system's environment variables. 1. In a separate terminal, run the `mongod` command to start the local instance of the database. 1. Create a folder named "data" in the C drive and within it create a new folder named "db". 1. Open a terminal and run the `mongosh` command in the terminal you will get the connection string. In this case the Connection String is: `mongodb://127.0.0.1:27017/?directConnection=true&serverSelectionTimeoutMS=2000&appName=mongosh+1.6.2` - 1. In the `.env` file of Talawa-API, add the connection string to the `MONGO_DB_URL` section. + 1. In the `.env` file of Talawa-API, add the connection string to the `MONGO_DB_URL` section. + ``` $ mongosh @@ -334,14 +355,16 @@ Here's the procedure to set up Redis. In the `.env` file, you should find three variables: `REDIS_HOST`, `REDIS_PORT`, and `REDIS_PASSWORD`. These environment variables will contain the necessary information for your codebase to connect to a running `redis-server`. -### For Local Setup (Linux and WSL) +### For Local Setup (Linux and WSL) + In both scenarios (Linux or WSL post-installation), the variable values should be configured as follows: 1. `REDIS_HOST` = localhost 2. `REDIS_PORT` = 6379 **Note**: This default port is used by the `redis-server`. However, if your `redis-server` operates on a different port, you must provide that port number. 3. `REDIS_PASSWORD` should be left empty, as passwords are unnecessary for local connections. -### For Remote Setup (Redis Cloud) +### For Remote Setup (Redis Cloud) + To begin, you must register for a free account on Redis Cloud. Following this step, you can proceed by selecting a database from the free tier, which comes with a 30MB data storage limit. Once completed, you can then access your Database by navigating to the `Databases` section. Here, you will find the option to view the overall settings of your free instance. ![Screenshot from 2023-08-18 12-08-35](https://github.com/kb-0311/talawa-api/assets/96020697/86ef137d-5a52-47fc-9075-3ded42b16aaf) @@ -351,19 +374,20 @@ Here are the configuration details: 1. `REDIS_HOST` = The `Public endpoint` assigned to your Database, excluding the `.com`. It will resemble something like `redis-13354.c169.us-east-1-1.ec2.cloud.redislabs.com`. The numerical value following this address is the port number. 2. `REDIS_PORT` = The number provided in the `Public Endpoint` after the colon (`:`), for instance: `13354`. 3. `REDIS_PASSWORD` = The `Default user password` located in the Security Section. + ## Setting up .env LAST_RESORT_SUPERADMIN_EMAIL parameter -The user with the email address set with this parameter will automatically be elevated to Super Admin status on registration. +The user with the email address set with this parameter will automatically be elevated to Super Admin status on registration. -1. When installing, set this to the email address of the person you want to be the very first Super Admin. - - This will usually be the email address of the person installing the software. -1. If this is not set you will not be able to administer the application. +1. When installing, set this to the email address of the person you want to be the very first Super Admin. + - This will usually be the email address of the person installing the software. +1. If this is not set you will not be able to administer the application. -If you don't set this parameter, then you'll need to follow the `Manually Adding The First Super Admin User` process discussed later in this document. +If you don't set this parameter, then you'll need to follow the `Manually Adding The First Super Admin User` process discussed later in this document. Set this value in the event that you need to elevate any of your users to be a Super Admin. -**NOTE** It is STRONGLY advised that you remove the email address once the initial installation and setup has been done. +**NOTE** It is STRONGLY advised that you remove the email address once the initial installation and setup has been done. ## Configuring Google ReCAPTCHA @@ -380,6 +404,7 @@ We use `reCAPTCHA` for two factor authentication (2FA). Follow these steps: 1. Copy the generated `Secret Key` to variable named `RECAPTCHA_SECRET_KEY` in `.env` file. ![Set up recaptcha page](./image/recaptcha_site_and_secret_key.webp) + 1. **NOTE**: Save the generated `Site key` as it will be used in `talawa-admin`. ### Setting up .env MAIL_USERNAME and MAIL_PASSWORD ReCAPTCHA Parameters @@ -392,8 +417,8 @@ The MAIL_USERNAME and MAIL_PASSWORD parameters are required to enable an app to 1. Select `Security`. 1. Under `Signing in to Google` section select `App Passwords`. 1. Click on `Select app` section and choose `Other(Custom name)`, enter `talawa` as the custom name and press `Generate` button. -1. Copy the 16 character generated app password to the variable named `MAIL_PASSWORD` in `.env` file. -1. Copy your usual gmail address to the variable named `MAIL_USERNAME` in `.env` file. +1. Copy the 16 character generated app password to the variable named `MAIL_PASSWORD` in `.env` file. +1. Copy your usual gmail address to the variable named `MAIL_USERNAME` in `.env` file. For more info refer to this [Google Answer](https://support.google.com/accounts/answer/185833). @@ -401,8 +426,9 @@ For more info refer to this [Google Answer](https://support.google.com/accounts/ For using SMTP server instead of Gmail, following steps need to be followed: -1. Set the ```IS_SMTP``` variable to ```true``` for example ```IS_SMTP=true``` +1. Set the `IS_SMTP` variable to `true` for example `IS_SMTP=true` 1. Go to your your SMTP server, and note the following variables: + ``` SMTP_HOST=your-smtp-server-hostname SMTP_PORT=your-smtp-server-port @@ -410,7 +436,9 @@ SMTP_USERNAME=your-smtp-username SMTP_PASSWORD=your-smtp-password SMTP_SSL_TLS=true-or-false ``` + For example: + ``` SMTP_HOST=smtp.hostgator.com SMTP_PORT=465 @@ -421,21 +449,26 @@ SMTP_SSL_TLS=true For more information on setting up a smtp server, here's a [useful article](https://sendgrid.com/blog/what-is-an-smtp-server/) -## Setting up Logger configurations *(optional)* +## Setting up Logger configurations _(optional)_ You can set up and customize logs by configuring the following parameters ### Setting up COLORIZE_LOGS in .env file -The parameter `COLORIZE_LOGS` is a boolean field and can be set to true or false. It customizes the log colorization formats displayed in console. You can set the value in `.env` file as + +The parameter `COLORIZE_LOGS` is a boolean field and can be set to true or false. It customizes the log colorization formats displayed in console. You can set the value in `.env` file as + ``` COLORIZE_LOGS = false ``` + If the parameter value is set to `true`, you should be able to see colorized logs in console, or else logs will display in the console's default simple format. ![Colorized logs in console](./image/colorize-logs.jpg) ### Setting up LOG_LEVEL in .env file + There are different logging levels that can be configured by setting this parameter. The severity order of levels are displayed numerically ascending from most important to least important.
+ ``` levels = { error: 0, @@ -447,6 +480,7 @@ There are different logging levels that can be configured by setting this parame silly: 6 } ``` +
On setting this parameter value, log messages are displayed in the console only if the `message.level` is less than or equal to setted `LOG_LEVEL`

For our application, the most appropriate setting is `LOG_LEVEL = info` since most of information logged on the console are error messages, warnings or info texts. @@ -504,6 +538,7 @@ The key generated in the previous step is in a format suitable for use in a mobi 1. The command will generate keys for the `iOS` and `android` platforms respectively and place them in the `firebase_options.dart` file. 1. Edit the `firebase_options.dart` file. 1. Add the parameters in the `static const FirebaseOptions android = FirebaseOptions` section of the `firebase_options.dart` file to the Talawa API `.env` file under the `androidFirebaseOptions` heading. + 1. Replace any parameters that are already there in that section. 1. Remove any trailing commas on the lines you have added. 1. Remove any leading spaces on the lines you have added. @@ -514,7 +549,9 @@ The key generated in the previous step is in a format suitable for use in a mobi messagingSenderId: '261699118608', projectId: 'talawa-thingy', storageBucket: 'talawa-thingy.appspot.com', + 1. Add the parameters in the `static const FirebaseOptions ios = FirebaseOptions` section of the `firebase_options.dart` file to the Talawa API `.env` file under the `iosFirebaseOptions` heading. Replace any paramters that are already there. + 1. Replace any parameters that are already there in that section. 1. Remove any trailing commas on the lines you have added. 1. Remove any leading spaces on the lines you have added. @@ -536,24 +573,24 @@ Talawa API contains a sample database importing function which can be used to im ## Syntax: -```npm run import:sample-data -- [args]``` +`npm run import:sample-data -- [args]` You can pass the following arguments while running this script. -- ```--format```: Cleans the database before import. **Add this flag with caution. It will delete all of the existing data inside the talawa database.** -- ```--items=```: Specify the items to add. - - Following ```items``` can be specified, separated with a comma ```,``` - - ```users```: For users collection - - ```organizations```: For organizations collection - - ```events```: For events collection - - ```posts```: For posts collection +- `--format`: Cleans the database before import. **Add this flag with caution. It will delete all of the existing data inside the talawa database.** +- `--items=`: Specify the items to add. + - Following `items` can be specified, separated with a comma `,` + - `users`: For users collection + - `organizations`: For organizations collection + - `events`: For events collection + - `posts`: For posts collection ## Examples: -- ```npm run import:sample-data```: This command will import the complete sample database without removing the existing data. -- ```npm run import:sample-data -- --format```: This command will import the complete sample database after removing the existing data. -- ```npm run import:sample-data -- --format --items=users,organizations```: This command will import the sample ```users``` and ```organizations``` collections after cleaning the existing data. -- ```npm run import:sample-data -- --items=users,organizations```: This command will import the sample ```users``` and ```organizations``` collections without cleaning the existing data. +- `npm run import:sample-data`: This command will import the complete sample database without removing the existing data. +- `npm run import:sample-data -- --format`: This command will import the complete sample database after removing the existing data. +- `npm run import:sample-data -- --format --items=users,organizations`: This command will import the sample `users` and `organizations` collections after cleaning the existing data. +- `npm run import:sample-data -- --items=users,organizations`: This command will import the sample `users` and `organizations` collections without cleaning the existing data. ## Sample Data Overview: @@ -561,21 +598,23 @@ The sample data contains organizations, users, events and posts. Here are the de ### User Accounts and Organizations: -| Email | Password | User Type | Joined Organization | Admin For | -|-----------------------|----------|------------|---------------------|---------------------| -| testuser1@example.com | Pass@123 | USER | Angel Foundation | None | -| testuser2@example.com | Pass@123 | USER | Angel Foundation | None | -| testuser3@example.com | Pass@123 | USER | Angel Foundation | None | -| testadmin1@example.com | Pass@123 | ADMIN | Angel Foundation | Angel Foundation | -| testadmin2@example.com | Pass@123 | ADMIN | Hope Foundation | Hope Foundation | -| testadmin3@example.com | Pass@123 | ADMIN | Dignity Foundation | Dignity Foundation | -| testsuperadmin@example.com | Pass@123 | SUPERADMIN | The Unity Foundation | ALL | +| Email | Password | User Type | Joined Organization | Admin For | +| -------------------------- | -------- | ---------- | -------------------- | ------------------ | +| testuser1@example.com | Pass@123 | USER | Angel Foundation | None | +| testuser2@example.com | Pass@123 | USER | Angel Foundation | None | +| testuser3@example.com | Pass@123 | USER | Angel Foundation | None | +| testadmin1@example.com | Pass@123 | ADMIN | Angel Foundation | Angel Foundation | +| testadmin2@example.com | Pass@123 | ADMIN | Hope Foundation | Hope Foundation | +| testadmin3@example.com | Pass@123 | ADMIN | Dignity Foundation | Dignity Foundation | +| testsuperadmin@example.com | Pass@123 | SUPERADMIN | The Unity Foundation | ALL | ### Posts -There is one post inside the ```The Unity Foundation``` + +There is one post inside the `The Unity Foundation` ### Events -There is one event inside the ```The Unity Foundation``` + +There is one event inside the `The Unity Foundation` # Running Talawa-API @@ -590,9 +629,11 @@ Run the following command to start talawa-api development server: npm run dev # How to Access the Talawa-API URL + There are many important URLs for accessing the API ## For Talawa-API Developers + By default talawa-api runs on `port 4000` on your system's localhost. It is available on the following endpoint: http://localhost:4000/ @@ -614,39 +655,42 @@ GraphQL endpoint for handling `subscriptions` is this: The Organization URL for Talawa mobile app developers will depend upon the device on which Mobile app is installed. ### On Android Virtual Device + - If Talawa Mobile App is installed on Android Virtual Device (AVD), use the following URL: + ``` http://10.0.2.2:4000/graphql ``` ### On a Real Mobile Device + - If Talawa Mobile App is installed on a Real Mobile Device, follow the below steps to get URL: - - Open Command Prompt in Windows, or Terminal in Linux/OSX - - Enter ```ipconfig``` (For Windows Users) or ```ifconfig``` (For Linux/OSX Users) - - Your Mobile and Computer (On which API server is running) must be on same Wifi Network. Use Mobile Hotspot to connect your computer to internet in case you don't have access to a Wifi Router. - - Search for the ```Wireless LAN adapter Wi-Fi:``` and then copy ```IPv4 Address```, like in image below: - - ![image](https://github.com/anshgoyalevil/talawa-api/blob/docs/image/ip-address.png) - - Now, use this IP address (```192.168.0.105``` in our case) to access the API instance using the following URL pattern: + - Open Command Prompt in Windows, or Terminal in Linux/OSX + - Enter `ipconfig` (For Windows Users) or `ifconfig` (For Linux/OSX Users) + - Your Mobile and Computer (On which API server is running) must be on same Wifi Network. Use Mobile Hotspot to connect your computer to internet in case you don't have access to a Wifi Router. + - Search for the `Wireless LAN adapter Wi-Fi:` and then copy `IPv4 Address`, like in image below: + - ![image](https://github.com/anshgoyalevil/talawa-api/blob/docs/image/ip-address.png) + - Now, use this IP address (`192.168.0.105` in our case) to access the API instance using the following URL pattern: ``` http://{IP_Address}:4000/graphql ``` + For example: + ``` http://192.168.0.105:4000/graphql ``` - ## For Talawa-Admin Developers The Organization URL for Talawa mobile app developers to use is: http://localhost:4000/graphql/ - # Accessing MongoDB -There are many ways to access MongoDB. +There are many ways to access MongoDB. ## Managing MongoDB using the MongoDB Compass GUI @@ -662,13 +706,13 @@ This guide is for `VSCode` users to easily manage their `MongoDB` databases: ![Install official mongoDB vscode extension](./image/install_mongodb_vscode_extension.webp) -2. Connect your `MongoDB` database to the extension. +2. Connect your `MongoDB` database to the extension. - ![Connect your mongoDB database to the extension](./image/connect_extension_to_mongodb_step_1.webp) + ![Connect your mongoDB database to the extension](./image/connect_extension_to_mongodb_step_1.webp) - ![Connect your mongoDB database to the extension](./image/connect_extension_to_mongodb_step_2.webp) + ![Connect your mongoDB database to the extension](./image/connect_extension_to_mongodb_step_2.webp) -3. Now you can manage the database you are using for `talawa-api` through this extension within `VSCode`. +3. Now you can manage the database you are using for `talawa-api` through this extension within `VSCode`. # Manually Adding The First Super Admin User @@ -676,41 +720,45 @@ You can skip these instructions for now if you don't have running instance of Ta 1. This step is for mandatory Linux specific users others can skip to next step: - 1. You need to start `mongod` [Mongo daemon process] for `mongosh` to work use the following command for the same: - - `sudo service mongod start` **[System V init(service)]** or `sudo systemctl start mongod` **[systemd(systemctl)]** - 2. To verify whether `mongod`[Mongo daemon process] is running you can use either: - - `sudo systemctl status mongod` **[systemd(systemctl)]** or `sudo service mongod status` **[System V init(service)]** + 1. You need to start `mongod` [Mongo daemon process] for `mongosh` to work use the following command for the same: + + - `sudo service mongod start` **[System V init(service)]** or `sudo systemctl start mongod` **[systemd(systemctl)]** + + 2. To verify whether `mongod`[Mongo daemon process] is running you can use either: + + - `sudo systemctl status mongod` **[systemd(systemctl)]** or `sudo service mongod status` **[System V init(service)]** ## Using MongoDB Compass 1. Open MongoDB Compass and click on `Connect`. 2. Select `user` collections and edit the data. Change: - 1. `userType` from `USER` to `SUPERADMIN` - 2. `adminApproved` from `false` to `true` - - ![Illustration for user edit ](./image/mongodb_compass_user_edit.png) - + 1. `userType` from `USER` to `SUPERADMIN` + 2. `adminApproved` from `false` to `true` + - ![Illustration for user edit ](./image/mongodb_compass_user_edit.png) + ## Using Mongo Shell - + 1. Open a terminal and run `mongosh` command to open interactive command line interface to work with MongoDB database. 2. In the `mongosh` terminal use the following command to edit the `users` collections data: - 1.Find the login credentials in the database through following command: - ``` - db.users.find({userType: 'USER', firstName: ''}) - ``` - 2. Elevate permission from `USER` to `SUPERADMIN` and set `adminApproved` to `true`: - ``` - db.users.updateOne({ firstName: '' },{ $set: { userType: 'SUPERADMIN', adminApproved: true }}) - ``` - 3. To verify the details were updated correctly use: - ``` - db.users.find({firstName:'' }) - ``` - + 1.Find the login credentials in the database through following command: + ``` + db.users.find({userType: 'USER', firstName: ''}) + ``` + 2. Elevate permission from `USER` to `SUPERADMIN` and set `adminApproved` to `true`: + ``` + db.users.updateOne({ firstName: '' },{ $set: { userType: 'SUPERADMIN', adminApproved: true }}) + ``` + 3. To verify the details were updated correctly use: + ``` + db.users.find({firstName:'' }) + ``` + **Note**: You can do the edits via any of the two methods. # Other + These are some other factors to consider ## Changing default talawa-api port @@ -731,7 +779,6 @@ will make talawa-api accessible on the following endpoint: http://localhost:5000/ - # Testing Talawa-api makes use of `vitest` to run tests because it is much faster than `jest` and more comfortable to work with. diff --git a/locales/en.json b/locales/en.json index ab4f46122e..8fb3a8dbc3 100644 --- a/locales/en.json +++ b/locales/en.json @@ -5,6 +5,7 @@ "user.alreadyMember": "User is already a member", "user.profileImage.notFound": "User profile image not found", "task.notFound": "Task not found", + "advertisement.notFound": "Advertisement not found", "event.notFound": "Event not found", "eventProject.notFound": "Event project not found", "organization.notFound": "Organization not found", diff --git a/locales/hi.json b/locales/hi.json index 8905275785..45eaaeeeed 100644 --- a/locales/hi.json +++ b/locales/hi.json @@ -5,6 +5,7 @@ "user.alreadyMember": "उपयोगकर्ता पहले से ही एक सदस्य है", "user.profileImage.notFound": "उपयोगकर्ता प्रोफ़ाइल छवि नहीं मिली", "task.notFound": "कार्य नहीं मिला", + "advertisement.notFound": "विज्ञापन नहीं मिला", "event.notFound": "घटना नहीं मिली", "eventProject.notFound": "इवेंट प्रोजेक्ट नहीं मिला", "organization.notFound": "संगठन नहीं मिला", diff --git a/schema.graphql b/schema.graphql index f1d037cdc1..944ebd0d27 100644 --- a/schema.graphql +++ b/schema.graphql @@ -2,6 +2,16 @@ directive @auth on FIELD_DEFINITION directive @role(requires: UserType) on FIELD_DEFINITION +type Advertisement { + _id: ID + endDate: Date! + link: String! + name: String! + orgId: ID + startDate: Date! + type: String! +} + type AggregatePost { count: Int! } @@ -422,6 +432,7 @@ type Mutation { cancelMembershipRequest(membershipRequestId: ID!): MembershipRequest! checkIn(data: CheckInInput!): CheckIn! createAdmin(data: UserAndOrganizationInput!): User! + createAdvertisement(endDate: Date!, link: String!, name: String!, orgId: ID!, startDate: Date!, type: String!): Advertisement! createComment(data: CommentInput!, postId: ID!): Comment createDirectChat(data: createChatInput!): DirectChat! createDonation(amount: Float!, nameOfOrg: String!, nameOfUser: String!, orgId: ID!, payPalId: ID!, userId: ID!): Donation! @@ -450,6 +461,7 @@ type Mutation { rejectAdmin(id: ID!): Boolean! rejectMembershipRequest(membershipRequestId: ID!): MembershipRequest! removeAdmin(data: UserAndOrganizationInput!): User! + removeAdvertisement(id: ID!): Advertisement removeComment(id: ID!): Comment removeDirectChat(chatId: ID!, organizationId: ID!): DirectChat! removeEvent(id: ID!): Event! @@ -722,6 +734,7 @@ type Query { event(id: ID!): Event eventsByOrganization(id: ID, orderBy: EventOrderByInput): [Event] eventsByOrganizationConnection(first: Int, orderBy: EventOrderByInput, skip: Int, where: EventWhereInput): [Event!]! + getAdvertisements: [Advertisement] getDonationById(id: ID!): Donation! getDonationByOrgId(orgId: ID!): [Donation] getDonationByOrgIdConnection(first: Int, orgId: ID!, skip: Int, where: DonationWhereInput): [Donation!]! diff --git a/src/constants.ts b/src/constants.ts index 46f486120c..851079990a 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -265,7 +265,12 @@ export const TASK_NOT_FOUND_ERROR = { MESSAGE: "task.notFound", PARAM: "task", }; - +export const ADVERTISEMENT_NOT_FOUND_ERROR = { + DESC: "Advertisement not found", + CODE: "advertisement.notFound", + MESSAGE: "advertisement.notFound", + PARAM: "advertisement", +}; export const STATUS_ACTIVE = "ACTIVE"; export const URL = diff --git a/src/models/Advertisement.ts b/src/models/Advertisement.ts new file mode 100644 index 0000000000..c80632e61f --- /dev/null +++ b/src/models/Advertisement.ts @@ -0,0 +1,81 @@ +import type { Types, Model } from "mongoose"; +import { Schema, model, models } from "mongoose"; +/** + * This is an interface that represents a database(MongoDB) document for Advertisement. + */ +type AdvertisementTypes = { + type: "POPUP" | "MENU" | "BANNER"; + // Other properties specific to each type +}; +export interface InterfaceAdvertisement { + _id: Types.ObjectId; + orgId: string; + name: string; + link: string; + type: AdvertisementTypes; + startDate: string; + endDate: string; +} + +/** + * @param name - Name of the advertisement (type: String) + * Description: Name of the advertisement. + */ + +/** + * @param orgId - Organization ID associated with the advertisement (type: Schema.Types.ObjectId) + * Description: Organization ID associated with the advertisement. + */ + +/** + * @param link - Link associated with the advertisement (type: String) + * Description: Link associated with the advertisement. + */ + +/** + * @param type - Type of advertisement (POPUP, MENU, BANNER) (type: String) + * Description: Type of advertisement (POPUP, MENU, BANNER). + */ + +/** + * @param startDate - Start date of the advertisement (type: Date) + * Description: Start date of the advertisement. + */ + +/** + * @param endDate - End date of the advertisement (type: Date) + * Description: End date of the advertisement. + */ +const advertisementSchema = new Schema({ + name: { + type: String, + required: true, + }, + orgId: { + type: String, + }, + link: { + type: String, + required: true, + }, + type: { + type: String, + enum: ["POPUP", "MENU", "BANNER"], + required: true, + }, + startDate: { + type: String, + required: true, + }, + endDate: { + type: String, + required: true, + }, +}); + +const advertisementModel = (): Model => + model("Advertisement", advertisementSchema); + +// This syntax is needed to prevent Mongoose OverwriteModelError while running tests. +export const Advertisement = (models.Advertisement || + advertisementModel()) as ReturnType; diff --git a/src/models/index.ts b/src/models/index.ts index 15bfe169b5..6b8b9039b1 100644 --- a/src/models/index.ts +++ b/src/models/index.ts @@ -1,3 +1,4 @@ +export * from "./Advertisement"; export * from "./CheckIn"; export * from "./MessageChat"; export * from "./Comment"; diff --git a/src/resolvers/Mutation/createAdvertisement.ts b/src/resolvers/Mutation/createAdvertisement.ts new file mode 100644 index 0000000000..10d2ed163b --- /dev/null +++ b/src/resolvers/Mutation/createAdvertisement.ts @@ -0,0 +1,18 @@ +import type { MutationResolvers } from "../../types/generatedGraphQLTypes"; +import { Advertisement } from "../../models"; +//eslint-disable-next-line @typescript-eslint/naming-convention +const { ObjectId } = require("mongodb"); +// @ts-ignore +export const createAdvertisement: MutationResolvers["createAdvertisement"] = + // eslint-disable-next-line @typescript-eslint/no-unused-vars + async (_parent, args, _context) => { + // Creates new Ad. + args.orgId = ObjectId(args.orgId); + args.startDate = new Date(args.startDate); + args.endDate = new Date(args.endDate); + const createdAd = await Advertisement.create({ + ...args, + }); + // Returns createdAd. + return createdAd.toObject(); + }; diff --git a/src/resolvers/Mutation/index.ts b/src/resolvers/Mutation/index.ts index 96d6e3534e..f94ab86637 100644 --- a/src/resolvers/Mutation/index.ts +++ b/src/resolvers/Mutation/index.ts @@ -25,6 +25,7 @@ import { createGroupChat } from "./createGroupChat"; import { createMessageChat } from "./createMessageChat"; import { createOrganization } from "./createOrganization"; import { createPlugin } from "./createPlugin"; +import { createAdvertisement } from "./createAdvertisement"; import { createPost } from "./createPost"; import { createTask } from "./createTask"; import { createUserTag } from "./createUserTag"; @@ -49,6 +50,7 @@ import { removeEvent } from "./removeEvent"; import { removeEventAttendee } from "./removeEventAttendee"; import { removeEventProject } from "./removeEventProject"; import { removeGroupChat } from "./removeGroupChat"; +import { removeAdvertisement } from "./removeAdvertisement"; import { removeMember } from "./removeMember"; import { removeOrganization } from "./removeOrganization"; import { removeOrganizationImage } from "./removeOrganizationImage"; @@ -101,6 +103,7 @@ export const Mutation: MutationResolvers = { createMember, createAdmin, createComment, + createAdvertisement, createDirectChat, createDonation, createEvent, @@ -131,6 +134,7 @@ export const Mutation: MutationResolvers = { removeDirectChat, removeEvent, removeEventAttendee, + removeAdvertisement, removeEventProject, removeGroupChat, removeMember, diff --git a/src/resolvers/Mutation/removeAdvertisement.ts b/src/resolvers/Mutation/removeAdvertisement.ts new file mode 100644 index 0000000000..9c9fa0059c --- /dev/null +++ b/src/resolvers/Mutation/removeAdvertisement.ts @@ -0,0 +1,28 @@ +import type { MutationResolvers } from "../../types/generatedGraphQLTypes"; +import { errors, requestContext } from "../../libraries"; +import { Advertisement } from "../../models"; +import { ADVERTISEMENT_NOT_FOUND_ERROR } from "../../constants"; + +// @ts-ignore +export const removeAdvertisement: MutationResolvers["removeAdvertisement"] = + // eslint-disable-next-line @typescript-eslint/no-unused-vars + async (_parent, args, _context) => { + const currentAd = await Advertisement.findOne({ + _id: args.id ? args.id : "", + }).lean(); + + if (!currentAd) { + throw new errors.NotFoundError( + requestContext.translate(ADVERTISEMENT_NOT_FOUND_ERROR.MESSAGE), + ADVERTISEMENT_NOT_FOUND_ERROR.CODE, + ADVERTISEMENT_NOT_FOUND_ERROR.PARAM + ); + } + + // Deletes the ad. + await Advertisement.deleteOne({ + _id: args.id ? args.id : "", + }); + // Returns deleted ad. + return currentAd; + }; diff --git a/src/resolvers/Query/getAdvertisements.ts b/src/resolvers/Query/getAdvertisements.ts new file mode 100644 index 0000000000..374d5b5346 --- /dev/null +++ b/src/resolvers/Query/getAdvertisements.ts @@ -0,0 +1,11 @@ +import type { QueryResolvers } from "../../types/generatedGraphQLTypes"; +import { Advertisement } from "../../models"; + +/** + * This function returns list of Advertisement from the database. + * @returns An object that contains a list of Ads. + */ +export const getAdvertisements: QueryResolvers["getAdvertisements"] = + async () => { + return await Advertisement.find().lean(); + }; diff --git a/src/resolvers/Query/index.ts b/src/resolvers/Query/index.ts index e9a306a33f..e43e7905eb 100644 --- a/src/resolvers/Query/index.ts +++ b/src/resolvers/Query/index.ts @@ -22,6 +22,7 @@ import { registeredEventsByUser } from "./registeredEventsByUser"; import { user } from "./user"; import { userLanguage } from "./userLanguage"; import { users } from "./users"; +import { getAdvertisements } from "./getAdvertisements"; import { usersConnection } from "./usersConnection"; export const Query: QueryResolvers = { @@ -32,6 +33,7 @@ export const Query: QueryResolvers = { eventsByOrganization, eventsByOrganizationConnection, getDonationById, + getAdvertisements, getDonationByOrgId, getDonationByOrgIdConnection, getlanguage, diff --git a/src/services/CommentCache/findCommentsByPostIdInCache.ts b/src/services/CommentCache/findCommentsByPostIdInCache.ts index 26377fab17..a04ac477c7 100644 --- a/src/services/CommentCache/findCommentsByPostIdInCache.ts +++ b/src/services/CommentCache/findCommentsByPostIdInCache.ts @@ -19,7 +19,7 @@ export async function findCommentsByPostIdInCache( const commentsFoundInCache = await CommentCache.mget(commentIDs); - const comments = commentsFoundInCache.map((comment) => { + const comments = commentsFoundInCache.map((comment: string | null) => { if (comment === null) { return null; } diff --git a/src/services/OrganizationCache/findOrganizationsInCache.ts b/src/services/OrganizationCache/findOrganizationsInCache.ts index bdf0e3b411..e25bf27037 100644 --- a/src/services/OrganizationCache/findOrganizationsInCache.ts +++ b/src/services/OrganizationCache/findOrganizationsInCache.ts @@ -12,7 +12,7 @@ export async function findOrganizationsInCache( const organizationFoundInCache = await OrganizationCache.mget(keys); - const organizations = organizationFoundInCache.map((org) => { + const organizations = organizationFoundInCache.map((org: string | null) => { if (org === null) { return null; } diff --git a/src/typeDefs/mutations.ts b/src/typeDefs/mutations.ts index 4873d27c0a..b048daeb2c 100644 --- a/src/typeDefs/mutations.ts +++ b/src/typeDefs/mutations.ts @@ -76,6 +76,15 @@ export const mutations = gql` uninstalledOrgs: [ID!] ): Plugin! + createAdvertisement( + orgId: ID! + name: String! + link: String! + type: String! + startDate: Date! + endDate: Date! + ): Advertisement! + createPost(data: PostInput!, file: String): Post @auth createUserTag(input: CreateUserTagInput!): UserTag @auth @@ -134,6 +143,8 @@ export const mutations = gql` removePost(id: ID!): Post @auth + removeAdvertisement(id: ID!): Advertisement + removeUserTag(id: ID!): UserTag @auth removeTask(id: ID!): Task @auth diff --git a/src/typeDefs/queries.ts b/src/typeDefs/queries.ts index f206980ac1..250d36afcd 100644 --- a/src/typeDefs/queries.ts +++ b/src/typeDefs/queries.ts @@ -38,6 +38,7 @@ export const queries = gql` getlanguage(lang_code: String!): [Translation] getPlugins: [Plugin] + getAdvertisements: [Advertisement] hasSubmittedFeedback(userId: ID!, eventId: ID!): Boolean diff --git a/src/typeDefs/types.ts b/src/typeDefs/types.ts index f1caf7c9cb..4b5027bd6f 100644 --- a/src/typeDefs/types.ts +++ b/src/typeDefs/types.ts @@ -92,6 +92,15 @@ export const types = gql` nameOfOrg: String! amount: Float! } + type Advertisement { + _id: ID + name: String! + orgId: ID + link: String! + type: String! + startDate: Date! + endDate: Date! + } type ExtendSession { accessToken: String! diff --git a/src/types/generatedGraphQLTypes.ts b/src/types/generatedGraphQLTypes.ts index bfd655c651..24d325967f 100644 --- a/src/types/generatedGraphQLTypes.ts +++ b/src/types/generatedGraphQLTypes.ts @@ -48,6 +48,17 @@ export type Scalars = { Upload: any; }; +export type Advertisement = { + __typename?: 'Advertisement'; + _id?: Maybe; + endDate: Scalars['Date']; + link: Scalars['String']; + name: Scalars['String']; + orgId?: Maybe; + startDate: Scalars['Date']; + type: Scalars['String']; +}; + export type AggregatePost = { __typename?: 'AggregatePost'; count: Scalars['Int']; @@ -493,6 +504,7 @@ export type Mutation = { cancelMembershipRequest: MembershipRequest; checkIn: CheckIn; createAdmin: User; + createAdvertisement: Advertisement; createComment?: Maybe; createDirectChat: DirectChat; createDonation: Donation; @@ -521,6 +533,7 @@ export type Mutation = { rejectAdmin: Scalars['Boolean']; rejectMembershipRequest: MembershipRequest; removeAdmin: User; + removeAdvertisement?: Maybe; removeComment?: Maybe; removeDirectChat: DirectChat; removeEvent: Event; @@ -646,6 +659,16 @@ export type MutationCreateAdminArgs = { }; +export type MutationCreateAdvertisementArgs = { + endDate: Scalars['Date']; + link: Scalars['String']; + name: Scalars['String']; + orgId: Scalars['ID']; + startDate: Scalars['Date']; + type: Scalars['String']; +}; + + export type MutationCreateCommentArgs = { data: CommentInput; postId: Scalars['ID']; @@ -793,6 +816,11 @@ export type MutationRemoveAdminArgs = { }; +export type MutationRemoveAdvertisementArgs = { + id: Scalars['ID']; +}; + + export type MutationRemoveCommentArgs = { id: Scalars['ID']; }; @@ -1235,6 +1263,7 @@ export type Query = { event?: Maybe; eventsByOrganization?: Maybe>>; eventsByOrganizationConnection: Array; + getAdvertisements?: Maybe>>; getDonationById: Donation; getDonationByOrgId?: Maybe>>; getDonationByOrgIdConnection: Array; @@ -1803,6 +1832,7 @@ export type DirectiveResolverFn; AggregatePost: ResolverTypeWrapper; AggregateUser: ResolverTypeWrapper; AndroidFirebaseOptions: ResolverTypeWrapper; @@ -1929,6 +1959,7 @@ export type ResolversTypes = { /** Mapping between all available schema types and the resolvers parents */ export type ResolversParentTypes = { + Advertisement: Advertisement; AggregatePost: AggregatePost; AggregateUser: AggregateUser; AndroidFirebaseOptions: AndroidFirebaseOptions; @@ -2053,6 +2084,17 @@ export type RoleDirectiveArgs = { export type RoleDirectiveResolver = DirectiveResolverFn; +export type AdvertisementResolvers = { + _id?: Resolver, ParentType, ContextType>; + endDate?: Resolver; + link?: Resolver; + name?: Resolver; + orgId?: Resolver, ParentType, ContextType>; + startDate?: Resolver; + type?: Resolver; + __isTypeOf?: IsTypeOfResolverFn; +}; + export type AggregatePostResolvers = { count?: Resolver; __isTypeOf?: IsTypeOfResolverFn; @@ -2371,6 +2413,7 @@ export type MutationResolvers>; checkIn?: Resolver>; createAdmin?: Resolver>; + createAdvertisement?: Resolver>; createComment?: Resolver, ParentType, ContextType, RequireFields>; createDirectChat?: Resolver>; createDonation?: Resolver>; @@ -2399,6 +2442,7 @@ export type MutationResolvers>; rejectMembershipRequest?: Resolver>; removeAdmin?: Resolver>; + removeAdvertisement?: Resolver, ParentType, ContextType, RequireFields>; removeComment?: Resolver, ParentType, ContextType, RequireFields>; removeDirectChat?: Resolver>; removeEvent?: Resolver>; @@ -2543,6 +2587,7 @@ export type QueryResolvers, ParentType, ContextType, RequireFields>; eventsByOrganization?: Resolver>>, ParentType, ContextType, Partial>; eventsByOrganizationConnection?: Resolver, ParentType, ContextType, Partial>; + getAdvertisements?: Resolver>>, ParentType, ContextType>; getDonationById?: Resolver>; getDonationByOrgId?: Resolver>>, ParentType, ContextType, RequireFields>; getDonationByOrgIdConnection?: Resolver, ParentType, ContextType, RequireFields>; @@ -2697,6 +2742,7 @@ export type UsersConnectionResultResolvers = { + Advertisement?: AdvertisementResolvers; AggregatePost?: AggregatePostResolvers; AggregateUser?: AggregateUserResolvers; AndroidFirebaseOptions?: AndroidFirebaseOptionsResolvers; diff --git a/tests/resolvers/Mutation/createAdvertisement.spec.ts b/tests/resolvers/Mutation/createAdvertisement.spec.ts new file mode 100644 index 0000000000..58b0c9de7e --- /dev/null +++ b/tests/resolvers/Mutation/createAdvertisement.spec.ts @@ -0,0 +1,86 @@ +import "dotenv/config"; +import type mongoose from "mongoose"; +import type { MutationCreateAdvertisementArgs } from "../../../src/types/generatedGraphQLTypes"; +import { connect, disconnect } from "../../helpers/db"; + +import { + beforeAll, + afterAll, + describe, + it, + expect, + afterEach, + vi, +} from "vitest"; +import type { + TestOrganizationType, + TestUserType, +} from "../../helpers/userAndOrg"; +import { + createTestUserAndOrganization, + createTestUser, +} from "../../helpers/userAndOrg"; +let testUser: TestUserType; +// eslint-disable-next-line @typescript-eslint/no-unused-vars +let randomUser: TestUserType; +// eslint-disable-next-line @typescript-eslint/no-unused-vars +let testOrganization: TestOrganizationType; +let MONGOOSE_INSTANCE: typeof mongoose; + +vi.mock("../../utilities/uploadEncodedImage", () => ({ + uploadEncodedImage: vi.fn(), +})); + +beforeAll(async () => { + MONGOOSE_INSTANCE = await connect(); + const temp = await createTestUserAndOrganization(); + testUser = temp[0]; + testOrganization = temp[1]; + randomUser = await createTestUser(); +}); + +afterAll(async () => { + await disconnect(MONGOOSE_INSTANCE); +}); + +describe("resolvers -> Mutation -> createAdvertisement", () => { + afterEach(() => { + vi.doUnmock("../../../src/constants"); + vi.resetModules(); + vi.resetAllMocks(); + }); + + it(`should create the ad and returns `, async () => { + const args: MutationCreateAdvertisementArgs = { + name: "myad", + orgId: "64d1f8cb77a4b61004f824b8", + type: "POPUP", + link: "https://www.example.com", + startDate: "2023-10-08T13:02:29.000Z", + endDate: "2023-10-08T13:02:29.000Z", + }; + + const context = { + userId: testUser?.id, + }; + + const { createAdvertisement: createAdvertisementResolver } = await import( + "../../../src/resolvers/Mutation/createAdvertisement" + ); + + const createdAdvertisementPayload = await createAdvertisementResolver?.( + {}, + args, + context + ); + + expect(createdAdvertisementPayload).toHaveProperty("name", "myad"); + + expect(createdAdvertisementPayload).toHaveProperty( + "link", + "https://www.example.com" + ); + + expect(createdAdvertisementPayload).toHaveProperty("type", "POPUP"); + }); +}); diff --git a/tests/resolvers/Mutation/getAdvertisement.spec.ts b/tests/resolvers/Mutation/getAdvertisement.spec.ts new file mode 100644 index 0000000000..34c99b9f4b --- /dev/null +++ b/tests/resolvers/Mutation/getAdvertisement.spec.ts @@ -0,0 +1,98 @@ +import "dotenv/config"; +import type mongoose from "mongoose"; +import type { MutationCreateAdvertisementArgs } from "../../../src/types/generatedGraphQLTypes"; +import { connect, disconnect } from "../../helpers/db"; + +import { + beforeAll, + afterAll, + describe, + it, + expect, + afterEach, + vi, +} from "vitest"; +import type { + TestOrganizationType, + TestUserType, +} from "../../helpers/userAndOrg"; +import { + createTestUserAndOrganization, + createTestUser, +} from "../../helpers/userAndOrg"; + +let testUser: TestUserType; +// eslint-disable-next-line @typescript-eslint/no-unused-vars +let randomUser: TestUserType; +// eslint-disable-next-line @typescript-eslint/no-unused-vars +let testOrganization: TestOrganizationType; +let MONGOOSE_INSTANCE: typeof mongoose; + +vi.mock("../../utilities/uploadEncodedImage", () => ({ + uploadEncodedImage: vi.fn(), +})); + +beforeAll(async () => { + MONGOOSE_INSTANCE = await connect(); + const temp = await createTestUserAndOrganization(); + testUser = temp[0]; + testOrganization = temp[1]; + randomUser = await createTestUser(); +}); + +afterAll(async () => { + await disconnect(MONGOOSE_INSTANCE); +}); + +describe("resolvers -> Mutation -> getAdvertisement", () => { + afterEach(() => { + vi.doUnmock("../../../src/constants"); + vi.resetModules(); + vi.resetAllMocks(); + }); + + it(`shouldl return the created ad from the list`, async () => { + const args: MutationCreateAdvertisementArgs = { + name: "myad", + orgId: "64d1f8cb77a4b61004f824b8", + type: "POPUP", + link: "https://www.example.com", + startDate: "2023-10-08T13:02:29.000Z", + endDate: "2023-10-08T13:02:29.000Z", + }; + + const context = { + userId: testUser?.id, + }; + + //creating a advertisement + const { createAdvertisement: createAdvertisementResolver } = await import( + "../../../src/resolvers/Mutation/createAdvertisement" + ); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const createdAdvertisementPayload = await createAdvertisementResolver?.( + {}, + args, + context + ); + + const { getAdvertisements: getAdvertisementResolver } = await import( + "../../../src/resolvers/Query/getAdvertisements" + ); + const getAdvertisementPayload = await getAdvertisementResolver?.( + {}, + args, + context + ); + if (getAdvertisementPayload) { + expect(getAdvertisementPayload[0]).toHaveProperty("name", "myad"); + + expect(getAdvertisementPayload[0]).toHaveProperty( + "link", + "https://www.example.com" + ); + + expect(getAdvertisementPayload[0]).toHaveProperty("type", "POPUP"); + } + }); +}); diff --git a/tests/resolvers/Mutation/removeAdvertisement.spec.ts b/tests/resolvers/Mutation/removeAdvertisement.spec.ts new file mode 100644 index 0000000000..79a05d8523 --- /dev/null +++ b/tests/resolvers/Mutation/removeAdvertisement.spec.ts @@ -0,0 +1,132 @@ +import "dotenv/config"; +import type mongoose from "mongoose"; +import type { MutationCreateAdvertisementArgs } from "../../../src/types/generatedGraphQLTypes"; +import { connect, disconnect } from "../../helpers/db"; + +import { + beforeAll, + afterAll, + describe, + it, + expect, + afterEach, + vi, +} from "vitest"; +import type { + TestOrganizationType, + TestUserType, +} from "../../helpers/userAndOrg"; +import { + createTestUserAndOrganization, + createTestUser, +} from "../../helpers/userAndOrg"; +import { ADVERTISEMENT_NOT_FOUND_ERROR } from "../../../src/constants"; + +let testUser: TestUserType; +// eslint-disable-next-line @typescript-eslint/no-unused-vars +let randomUser: TestUserType; +// eslint-disable-next-line @typescript-eslint/no-unused-vars +let testOrganization: TestOrganizationType; +let MONGOOSE_INSTANCE: typeof mongoose; + +vi.mock("../../utilities/uploadEncodedImage", () => ({ + uploadEncodedImage: vi.fn(), +})); + +beforeAll(async () => { + MONGOOSE_INSTANCE = await connect(); + const temp = await createTestUserAndOrganization(); + testUser = temp[0]; + testOrganization = temp[1]; + randomUser = await createTestUser(); +}); + +afterAll(async () => { + await disconnect(MONGOOSE_INSTANCE); +}); + +describe("resolvers -> Mutation -> removeAdvertisement", () => { + afterEach(() => { + vi.doUnmock("../../../src/constants"); + vi.resetModules(); + vi.resetAllMocks(); + }); + + it(`creates the ad and then deleting the ad`, async () => { + const args: MutationCreateAdvertisementArgs = { + name: "myad", + orgId: "64d1f8cb77a4b61004f824b8", + type: "POPUP", + link: "https://www.example.com", + startDate: "2023-10-08T13:02:29.000Z", + endDate: "2023-10-08T13:02:29.000Z", + }; + + const context = { + userId: testUser?.id, + }; + + const { createAdvertisement: createAdvertisementResolver } = await import( + "../../../src/resolvers/Mutation/createAdvertisement" + ); + + const createdAdvertisementPayload = await createAdvertisementResolver?.( + {}, + args, + context + ); + const createdAdvertisementId = createdAdvertisementPayload?._id || ""; + + // deleting the ad + const { removeAdvertisement } = await import( + "../../../src/resolvers/Mutation/removeAdvertisement" + ); + + const removeAdvertisementPayload = await removeAdvertisement?.( + {}, + { id: createdAdvertisementId }, + context + ); + + expect(removeAdvertisementPayload).toHaveProperty( + "_id", + createdAdvertisementId + ); + + expect(removeAdvertisementPayload).toHaveProperty("name", "myad"); + + expect(removeAdvertisementPayload).toHaveProperty( + "link", + "https://www.example.com" + ); + + expect(removeAdvertisementPayload).toHaveProperty("type", "POPUP"); + }); + it("should throw NOT_FOUND_ERROR on wrong advertisement", async () => { + // deleting + const { removeAdvertisement } = await import( + "../../../src/resolvers/Mutation/removeAdvertisement" + ); + const context = { + userId: testUser?.id, + }; + const { requestContext } = await import("../../../src/libraries"); + const spy = vi + .spyOn(requestContext, "translate") + .mockImplementationOnce((message) => `Translated ${message}`); + + try { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const removeAdvertisementPayload = await removeAdvertisement?.( + {}, + { id: "64d1f8cb77a4b51004f824b8" }, + context + ); + } catch (error: any) { + expect(spy).toBeCalledWith(ADVERTISEMENT_NOT_FOUND_ERROR.MESSAGE); + expect(error.message).toEqual( + `Translated ${ADVERTISEMENT_NOT_FOUND_ERROR.MESSAGE}` + ); + } + }); +});