diff --git a/.github/workflows/run_tests.yaml b/.github/workflows/run_tests.yaml index a632bc95..8bd87a47 100644 --- a/.github/workflows/run_tests.yaml +++ b/.github/workflows/run_tests.yaml @@ -4,15 +4,51 @@ on: pull_request: branches: - master + - milestone/* + env: ENVIRONMENT: test - AWS_ACCESS_KEY_ID: local - AWS_SECRET_ACCESS_KEY: local + AWS_ACCESS_KEY_ID: ${{ secrets.PLG_PROD_ACCESS_KEY }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.PLG_PROD_SECRET_KEY }} AWS_REGION: us-east-1 jobs: test: runs-on: ubuntu-latest + services: + memcached: + image: memcached:1.6.21 + ports: + - 11211:11211 + + dynamodb: + image: amazon/dynamodb-local:latest + ports: + - 8000:8000 + steps: + - uses: actions/checkout@v3 + + - name: Set up JDK 17 + uses: actions/setup-java@v2 + with: + java-version: '17' + distribution: 'adopt' + + - name: Build Custom docker file for KMS + run: | + echo "Building docker image" + docker build -t local-kms -f Dockerfile.github . + echo "Starting docker container for local-kms" + docker run -d --name localkms -p 4599:8080 -e AWS_ACCESS_KEY_ID=${{ env.AWS_ACCESS_KEY_ID }} -e AWS_SECRET_ACCESS_KEY=${{ env.AWS_SECRET_ACCESS_KEY }} -e AWS_REGION=us-east-1 -e KMS_ACCOUNT_ID=111122223333 -e KMS_REGION=us-east-1 -e KMS_SEED_PATH=seed.yaml local-kms + + - name: Log into Docker Container + run: | + echo "Checking seed path" + seed_path=$(docker exec localkms ls /init) + echo "Seed path: $seed_path" + - run: |- - echo "Running test cases" \ No newline at end of file + echo "Start: run test cases" + sh ./mvnw clean test + echo "Done: run test cases" diff --git a/.gitignore b/.gitignore index 4bee3dd4..7b369379 100644 --- a/.gitignore +++ b/.gitignore @@ -38,6 +38,8 @@ build/ ### Environment Variables ### .env set*_env_vars.sh +secrets.json +test.secrets.json ### Logs files ### *.log diff --git a/Dockerfile.github b/Dockerfile.github new file mode 100644 index 00000000..613afe5e --- /dev/null +++ b/Dockerfile.github @@ -0,0 +1,7 @@ +FROM nsmithuk/local-kms + +EXPOSE 8080 + +# Add a file to the image +WORKDIR /init +COPY init/seed.yaml /init diff --git a/Dockerfile.local b/Dockerfile.local index a2cf669e..1d9ad1e9 100644 --- a/Dockerfile.local +++ b/Dockerfile.local @@ -1,14 +1,19 @@ -FROM openjdk:17-jdk-alpine -# Set working directory +# Use the openjdk 17 with alpine as a base image +FROM openjdk:17-jdk-alpine as builder +# Create and set the working directory inside the container RUN mkdir -p /app WORKDIR /app -COPY . /app +# Install bash for Alpine and other necessary tools +RUN apk add --no-cache bash maven -# Expose port 8080 for the app to listen on -EXPOSE 8080 -# Start the app -RUN apk add --no-cache bash +# Copy the pom.xml and download dependencies +COPY ./pom.xml ./pom.xml +RUN mvn dependency:go-offline -CMD ["sh", "-c", "start_local.sh"] \ No newline at end of file +# Copy the rest of the application +COPY ./ ./ + +# Expose the port 8080 for the application +EXPOSE 8080 \ No newline at end of file diff --git a/LOCAL_SETUP.md b/LOCAL_SETUP.md deleted file mode 100644 index 6161cf16..00000000 --- a/LOCAL_SETUP.md +++ /dev/null @@ -1,60 +0,0 @@ -### Prerequisites and System Requirements - -Before using the *Sales Sparrow* apis, make sure your development environment meets the following prerequisites and system requirements: -* For Docker **(Recommended)** - - Docker version 4.19.0 or newer -* Without Docker - - Java 17 - - You can download it from [here](https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html). - - Dynamo DB Local Setup - - Download the dynamodb local jar(noSQL Workbench) from [here](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/workbench.settingup.html) - - Install and run the noSQL Workbench on your local machine - - Toggle *DDB local* option from bottom of the left sidemenu to turn it on. - - Add a local connection from the operation builder tab - -## Getting Started -To clone the project and install dependencies, follow these steps: - -### Clone the project - -``` -$ git clone git@github.com:TrueSparrowSystems/AI-SalesSparrow-API.git -$ cd AI-SalesSparrow-API -``` - -### Clone the AI-SalesSparrow-Docs submodule ### -``` -$ git submodule update --init -``` - -### Set Environment Variables -1. Copy the contents of set_env_vars-sample.sh to set_env_vars.sh -2. Update the values of the environment variables in set_env_vars.sh - - Salesforce credentials needs to be copied from the salesforce connected app - - Aws credentials needs to be copied from the aws account - -### Start servers with docker -``` -$ docker-compose up -``` - -### Start servers without docker -``` -$ source set_env_vars.sh -$ ./mvnw spring-boot:run - ``` - - **To install new dependencies** - ``` - $ ./mvnw clean install -Dmaven.test.skip - ``` - - **To run test cases** - ``` - $ ./mvnw clean test - ``` - - **To run test cases and generate coverage report** - ``` - $ ./mvnw clean test jacoco:report - ``` \ No newline at end of file diff --git a/README.md b/README.md index b6474fd3..ceca2573 100644 --- a/README.md +++ b/README.md @@ -3,17 +3,26 @@ This repository contains salessparrow apis. ## Local Setup -Please follow the steps in [LOCAL_SETUP](LOCAL_SETUP.md) to setup the project locally. +Follow the steps in [LOCAL_SETUP](repo-docs/LOCAL_SETUP.md) to setup the project locally. + +## Environment Variables +Refer to [ENVIRONMENT_VARS](repo-docs/ENVIRONMENT_VARS.md) for detailed descriptions for the environment variables. ## Project Structure -Please refer to [PROJECT_STRUCTURE](PROJECT_STRUCTURE.md) for details on the project structure. +Refer to [PROJECT_STRUCTURE](repo-docs/PROJECT_STRUCTURE.md) for details on the project structure. + +## Changelog +Stay up-to-date with the latest changes and improvements in our API by referring to our [CHANGELOG](repo-docs/CHANGELOG.md). + +## Release Process +Refer to [RELEASE_PROCESS](repo-docs/RELEASE_PROCESS.md) to ensure smooth and consistent software releases. This process outlines the steps to prepare, package, and distribute new versions of our software. ## License This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details. ## Contributing -Contributions to the project are welcome! Please see our guidelines on how to [CONTRIBUTE](CONTRIBUTING.md). +Contributions to the project are welcome! Please see our guidelines on how to [CONTRIBUTE](repo-docs/CONTRIBUTING.md). ## Code of Conduct -This project has a code of conduct that outlines expected behavior for contributors and users. Please read the [CODE_OF_CONDUCT](CODE_OF_CONDUCT.md) file before getting involved. \ No newline at end of file +This project has a code of conduct that outlines expected behavior for contributors and users. Please read the [CODE_OF_CONDUCT](repo-docs/CODE_OF_CONDUCT.md) file before getting involved. \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 10902f9e..4b410fe6 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,38 +1,83 @@ -version: '3' +version: "3" services: - # App server provisioning api: build: context: . dockerfile: Dockerfile.local ports: - - '8080:8080' + - "8080:8080" networks: - localdev volumes: - - './:/app' - command: bash -c 'sh start_local.sh' + - "./:/app" + - "~/.m2:/root/.m2" + environment: + - ENVIRONMENT=development + - AWS_ACCESS_KEY_ID=local + - AWS_SECRET_ACCESS_KEY=local + - AWS_REGION=us-east-1 + command: ["./mvnw", "spring-boot:run"] + depends_on: + - dynamodb + - localkms + - memcached + + test: + build: + context: . + dockerfile: Dockerfile.local + ports: + - "8080:8080" + networks: + - localdev + volumes: + - "./:/app" + - "~/.m2:/root/.m2" + environment: + - ENVIRONMENT=local-test + - AWS_ACCESS_KEY_ID=local + - AWS_SECRET_ACCESS_KEY=local + - AWS_REGION=us-east-1 + command: ["./mvnw", "clean", "test", "jacoco:report"] + depends_on: + - dynamodb + - localkms + - memcached - # Memcached provisioning memcached: image: memcached ports: - - '11211:11211' + - "11211:11211" networks: - localdev - - # DynamoDB provisioning + + localkms: + image: nsmithuk/local-kms + ports: + - "4599:8080" + volumes: + - ./init:/init + networks: + - localdev + environment: + - AWS_ACCESS_KEY_ID='local' + - AWS_SECRET_ACCESS_KEY='local' + - AWS_REGION='us-east-1' + - KMS_ACCOUNT_ID='111122223333' + - KMS_REGION='us-east-1' + - KMS_SEED_PATH=init/seed.yaml + dynamodb: image: amazon/dynamodb-local:latest ports: - - '8000:8000' + - "8000:8000" networks: - localdev volumes: - - "./docker/dynamodb:/home/dynamodblocal/data" + - "./docker/dynamodb:/home/dynamodblocal/data" working_dir: /home/dynamodblocal - command: '-jar DynamoDBLocal.jar -sharedDb -dbPath ./data' + command: "-jar DynamoDBLocal.jar -sharedDb -dbPath ./data" networks: localdev: - driver: bridge \ No newline at end of file + driver: bridge diff --git a/docs b/docs index 17628f44..8e2977e2 160000 --- a/docs +++ b/docs @@ -1 +1 @@ -Subproject commit 17628f44f8389830e4f4290064ba539c1863d385 +Subproject commit 8e2977e29d59a9858d78369ea267bd86259bc839 diff --git a/init/seed.yaml b/init/seed.yaml new file mode 100644 index 00000000..9e4ff9d1 --- /dev/null +++ b/init/seed.yaml @@ -0,0 +1,17 @@ +# This file is used to seed the local KMS instance with keys and aliases. +# The keys and aliases defined here will be created when the KMS instance is +# first started. If the keys or aliases already exist, they will not be +# modified. +# This is for testing purposes only. + +Keys: + Symmetric: + Aes: + - Metadata: + KeyId: bc436485-5092-42b8-92a3-0aa8b93536dc + BackingKeys: + - 5cdaead27fe7da2de47945d73cd6d79e36494e73802f3cd3869f1d2cb0b5d7a9 + +Aliases: + - AliasName: alias/testing + TargetKeyId: bc436485-5092-42b8-92a3-0aa8b93536dc diff --git a/pom.xml b/pom.xml index 17f637e0..c9e70ecb 100644 --- a/pom.xml +++ b/pom.xml @@ -12,9 +12,9 @@ com.salessparrow - salessparrow-api - 0.0.1 - api + salessparrow-api + 0.2.0 + api Salessparrow apis @@ -30,22 +30,33 @@ 3.1.1 - - org.springframework.boot - spring-boot-starter-webflux + + org.springframework.boot + spring-boot-starter-webflux 3.1.1 - - + + + + org.springframework.boot + spring-boot-starter-security + 3.1.1 + + + org.springframework.security + spring-security-test + test + + org.modelmapper modelmapper 3.1.1 - + - com.fasterxml.jackson.core - jackson-databind - 2.15.2 + com.fasterxml.jackson.core + jackson-databind + 2.15.2 @@ -56,10 +67,16 @@ - org.springframework.boot - spring-boot-starter-validation + org.springframework.boot + spring-boot-starter-validation 3.1.1 - + + + + javax.xml.bind + jaxb-api + 2.4.0-b180830.0359 + com.amazonaws @@ -67,35 +84,35 @@ 1.12.523 - - com.amazonaws - aws-java-sdk-elasticache - 1.12.531 - + + com.amazonaws + aws-java-sdk-elasticache + 1.12.531 + - - org.hibernate.validator - hibernate-validator - 8.0.1.Final - + + org.hibernate.validator + hibernate-validator + 8.0.1.Final + - - org.springframework.boot - spring-boot-starter-cache + + org.springframework.boot + spring-boot-starter-cache 3.1.1 - + - - net.spy - spymemcached - 2.12.3 - + + net.spy + spymemcached + 2.12.3 + - org.slf4j - slf4j-nop - 2.0.6 - + org.slf4j + slf4j-nop + 2.0.6 + net.logstash.logback @@ -128,6 +145,30 @@ 0.6 + + com.amazonaws.secretsmanager + aws-secretsmanager-caching-java + 2.0.0 + + + + software.amazon.awssdk + secretsmanager + 2.20.132 + + + + io.spring.javaformat + spring-javaformat-maven-plugin + 0.0.39 + + + + org.jsoup + jsoup + 1.16.1 + + @@ -137,28 +178,40 @@ spring-boot-maven-plugin - - org.jacoco - jacoco-maven-plugin - 0.8.9 - - - - prepare-agent - - - - - report - test - - report - - - - - + + io.spring.javaformat + spring-javaformat-maven-plugin + 0.0.39 + + + + validate + apply + + + + + + org.jacoco + jacoco-maven-plugin + 0.8.9 + + + + prepare-agent + + + + + report + test + + report + + + + + - diff --git a/repo-docs/CHANGELOG.md b/repo-docs/CHANGELOG.md new file mode 100644 index 00000000..50bd86b5 --- /dev/null +++ b/repo-docs/CHANGELOG.md @@ -0,0 +1,58 @@ +# SalesSparrow APIs + +## 0.2.0 + +### New Features and Enhancements: + +- API - Disconnect Salesforce Endpoint [#9](https://github.com/TrueSparrowSystems/AI-SalesSparrow-API/issues/9) +- API - Get Recommended Actions from Text Endpoint [#13](https://github.com/TrueSparrowSystems/AI-SalesSparrow-API/issues/13) +- API - Delete Note Endpoint [#28](https://github.com/TrueSparrowSystems/AI-SalesSparrow-API/issues/28) +- API - Get a list of Tasks in an account [#33](https://github.com/TrueSparrowSystems/AI-SalesSparrow-API/issues/33) +- API - Create Task in an Account Endpoint [#34](https://github.com/TrueSparrowSystems/AI-SalesSparrow-API/issues/34) +- API - Get a list of CRM Organization Users Endpoint [#35](https://github.com/TrueSparrowSystems/AI-SalesSparrow-API/issues/35) +- Load Secrets from AWS during app initialization [#39](https://github.com/TrueSparrowSystems/AI-SalesSparrow-API/issues/39) +- API - Get Accounts Feed Endpoint [#45](https://github.com/TrueSparrowSystems/AI-SalesSparrow-API/issues/45) +- Enhance Code Security with Spring Boot [#53](https://github.com/TrueSparrowSystems/AI-SalesSparrow-API/issues/53) +- API - Delete Task in an Account Endpoint [#59](https://github.com/TrueSparrowSystems/AI-SalesSparrow-API/issues/59) +- Add CHANGELOG.md [#62](https://github.com/TrueSparrowSystems/AI-SalesSparrow-API/issues/62) +- Add RELEASE_PROCESS.md [#63](https://github.com/TrueSparrowSystems/AI-SalesSparrow-API/issues/63) +- Add Sanitization Filter for Request Params, Request Body, and Headers [#87](https://github.com/TrueSparrowSystems/AI-SalesSparrow-API/issues/87) + +### Bug Fixes: + +- Bug: DynamoDB Upsert Behavior Updates All Fields Instead of Specific Ones [#41](https://github.com/TrueSparrowSystems/AI-SalesSparrow-API/issues/41) + +### Miscellaneous: + +- Various code optimizations, refactoring, and improvements for better performance and maintainability. + +--- + +## 0.1.0 + +### Features: + +- Salesforce Connected App Setup [#1](https://github.com/TrueSparrowSystems/AI-SalesSparrow-API/issues/1) +- API - Get Salesforce OAuth URL Endpoint [#2](https://github.com/TrueSparrowSystems/AI-SalesSparrow-API/issues/2) +- Initial Project Setup [#3](https://github.com/TrueSparrowSystems/AI-SalesSparrow-API/issues/3) +- Sequence Diagram - Salesforce OAuth Flow [#4](https://github.com/TrueSparrowSystems/AI-SalesSparrow-API/issues/4) +- API - Post Salesforce Connect Endpoint [#5](https://github.com/TrueSparrowSystems/AI-SalesSparrow-API/issues/5) +- Initial Setup - Add Support for DynamoDB [#6](https://github.com/TrueSparrowSystems/AI-SalesSparrow-API/issues/6) +- API - Get Current User Endpoint [#7](https://github.com/TrueSparrowSystems/AI-SalesSparrow-API/issues/7) +- API - User Logout Endpoint [#8](https://github.com/TrueSparrowSystems/AI-SalesSparrow-API/issues/8) +- Initial Documentation [#10](https://github.com/TrueSparrowSystems/AI-SalesSparrow-API/issues/10) +- API - Implement Salesforce Composite API [#11](https://github.com/TrueSparrowSystems/AI-SalesSparrow-API/issues/11) +- API - Get Content Notes of an Account Endpoint [#12](https://github.com/TrueSparrowSystems/AI-SalesSparrow-API/issues/12) +- API - Search Accounts Endpoint [#15](https://github.com/TrueSparrowSystems/AI-SalesSparrow-API/issues/15) +- API - Create Note Endpoint [#16](https://github.com/TrueSparrowSystems/AI-SalesSparrow-API/issues/16) +- API - Get Note By Id Endpoint [#17](https://github.com/TrueSparrowSystems/AI-SalesSparrow-API/issues/17) +- Updated status enum values [#27](https://github.com/TrueSparrowSystems/AI-SalesSparrow-API/pull/27) + +--- + +Please feel free to provide feedback, report issues, and contribute to the SalesSparrow project! + +Thank you for using SalesSparrow. + + + diff --git a/CODE_OF_CONDUCT.md b/repo-docs/CODE_OF_CONDUCT.md similarity index 100% rename from CODE_OF_CONDUCT.md rename to repo-docs/CODE_OF_CONDUCT.md diff --git a/CONTRIBUTING.md b/repo-docs/CONTRIBUTING.md similarity index 100% rename from CONTRIBUTING.md rename to repo-docs/CONTRIBUTING.md diff --git a/repo-docs/ENVIRONMENT_VARS.md b/repo-docs/ENVIRONMENT_VARS.md new file mode 100644 index 00000000..474686d2 --- /dev/null +++ b/repo-docs/ENVIRONMENT_VARS.md @@ -0,0 +1,88 @@ +# Environment Variables Description + +This document provides descriptions for the environment variables used in the configuration of the Sales Sparrow api. These variables are essential for configuring and customizing the behavior of the application. + +## ENCRYPTION_KEY + +- **Description:** A key used for encryption purposes. This is a random string with alphanumeric values. +- **Example Value:** `7Fh9E2jRwDpV6gYcBqXsL` + +## API_COOKIE_SECRET + +- **Description:** Secret key for encrypting API cookies. This is a random string with alphanumeric values. +- **Example Value:** `7Fh9E2jRwDpV6gYcBqXsL` + +## AWS_IAM_REGION + +- **Description:** The AWS region for IAM (Identity and Access Management) services. +- **Example Value:** `us-east-1` + +## KMS_KEY_ID + +- **Description:** Amazon Resource Name (ARN) of the KMS (Key Management Service) key used for encryption. +- **Example Value:** `arn:aws:kms:'us-east-1':'111122223333':key/bc436485-5092-42b8-92a3-0aa8b93536dc` + +## SALESFORCE_CLIENT_ID + +- **Description:** Client ID for authenticating with Salesforce APIs. +- **Example Value:** `12345asdf` + +## SALESFORCE_CLIENT_SECRET + +- **Description:** Client secret for authenticating with Salesforce APIs. +- **Example Value:** `12345asdf` + +## SALESFORCE_AUTH_URL + +- **Description:** URL for Salesforce authentication. +- **Example Value:** `https://test.salesforce.com` + +## SALESFORCE_WHITELISTED_REDIRECT_URIS + +- **Description:** Comma-separated list of URIs allowed as redirect URIs for Salesforce authentication. +- **Example Value:** `http://localhost:3000,example://oauth/success` + +## MEMCACHED_CACHE_HOST + +- **Description:** Hostname of the Memcached cache server. +- **Example Value:** `memcached` + +## MEMCACHED_CACHE_PORT + +- **Description:** Port number for connecting to the Memcached cache server. +- **Example Value:** `11211` + +## DYNAMO_DB_URL + +- **Description:** URL for connecting to the DynamoDB server. +- **Example Value:** `http://dynamodb:8000` + +## LOCAL_KMS_ENDPOINT + +- **Description:** Endpoint for the local KMS (Key Management Service) server. +- **Example Value:** `http://localkms:8080` + +## ERROR_MAIL_FROM + +- **Description:** Email address used as the sender for error notifications. +- **Example Value:** `fromtest@test.com` + +## ERROR_MAIL_TO + +- **Description:** Email address where error notifications are sent. +- **Example Value:** `totest@test.com` + +## LOG_LEVEL + +- **Description:** Logging level for the application. +- **Example Value:** `debug` + +## COOKIE_DOMAIN + +- **Description:** Domain for the API cookies. +- **Example Value:** `localhost` + +## OPENAI_API_KEY + +- **Description:** API key for the OpenAI API. +- **Example Value:** `sk-12345asdf` \ No newline at end of file diff --git a/repo-docs/LOCAL_SETUP.md b/repo-docs/LOCAL_SETUP.md new file mode 100644 index 00000000..b17e3d94 --- /dev/null +++ b/repo-docs/LOCAL_SETUP.md @@ -0,0 +1,90 @@ +# Sales Sparrow APIs: Getting Started Guide + +## Prerequisites and System Requirements + +Before using the *Sales Sparrow* APIs, make sure your development environment meets the following prerequisites and system requirements: + +### Docker + +- Docker version 4.19.0 or newer + +## Getting Started + +To clone the project and install dependencies, follow these steps: + +### Clone the Project + +```sh +$ git clone git@github.com:TrueSparrowSystems/AI-SalesSparrow-API.git + +$ cd AI-SalesSparrow-API +``` + +### Clone the AI-SalesSparrow-Docs Submodule + +```sh +$ git submodule update --init +``` + +### Set Environment Variables + +1. Copy the contents of `sample.secrets.json` to `secrets.json`. +2. Update the values of the environment variables in secrets.json: + - Copy Salesforce credentials from the Salesforce connected app and update `SALESFORCE_CLIENT_ID`, `SALESFORCE_CLIENT_SECRET`, `SALESFORCE_AUTH_URL`. + - Update `KMS_KEY_ID` with value `arn:aws:kms:'us-east-1':'111122223333':key/bc436485-5092-42b8-92a3-0aa8b93536dc`. This local Docker setup uses the [local-kms](https://hub.docker.com/r/nsmithuk/local-kms) Docker image, and the key is already configured using [seed](init/seed.yaml). + +### Start the API Server with Docker + +```sh +$ docker-compose up api +``` + +### Run Test Cases with Docker + +#### Set Test-Related Environment Variables + +1. Create a `test.secrets.json` file: + +```sh +$ touch test.secrets.json +``` + +2. Add the following environment variables to `test.secrets.json`: + +```json +{ + "ENCRYPTION_KEY": "1234567890", + "API_COOKIE_SECRET": "1234567890", + "AWS_IAM_REGION": "us-east-1", + "KMS_KEY_ID": "arn:aws:kms:'us-east-1':'111122223333':key/bc436485-5092-42b8-92a3-0aa8b93536dc", + "SALESFORCE_CLIENT_ID": "12345", + "SALESFORCE_CLIENT_SECRET": "12345", + "SALESFORCE_AUTH_URL": "https://test.salesforce.com", + "SALESFORCE_WHITELISTED_REDIRECT_URIS": "http://localhost:3000", + "MEMCACHED_CACHE_HOST": "memcached", + "MEMCACHED_CACHE_PORT": "11211", + "LOG_LEVEL": "debug", + "DYNAMO_DB_URL": "http://dynamodb:8000", + "LOCAL_KMS_ENDPOINT": "http://localkms:8080", + "ERROR_MAIL_FROM": "", + "ERROR_MAIL_TO": "", + "COOKIE_DOMAIN": "", + "OPENAI_API_KEY": "" +} +``` + +#### Apply Spring Java Format + +Before committing the changes, it's good practice to apply the Spring Java format to your code to ensure it adheres to a consistent style. + +```sh +$ ./mvnw spring-javaformat:apply +``` + +#### Run Test Cases + +```sh +$ docker-compose up test +``` + +To view the test coverage, simply open the target/site/index.html file in a web browser. diff --git a/PROJECT_STRUCTURE.md b/repo-docs/PROJECT_STRUCTURE.md similarity index 95% rename from PROJECT_STRUCTURE.md rename to repo-docs/PROJECT_STRUCTURE.md index 71eb05d4..1523242f 100644 --- a/PROJECT_STRUCTURE.md +++ b/repo-docs/PROJECT_STRUCTURE.md @@ -29,6 +29,9 @@ This is a Markdown file that usually contains important information about your p This directory serves as a submodule linked to [ai-salesparrow-docs](https://github.com/TrueSparrowSystems/AI-SalesSparrow-docs) repository that holds comprehensive documentation for the project. Instead of duplicating documentation within the main repository, this submodule approach allows you to maintain documentation separately, ensuring consistency and easier management. +### repo-docs/ +This directory contains documentation that serves as a reference for various aspects of the project. This directory is intended to store informative documents that provide insights, explanations, and guidelines related to the project. + ## src/ Directory: ``` diff --git a/repo-docs/RELEASE_PROCESS.md b/repo-docs/RELEASE_PROCESS.md new file mode 100644 index 00000000..f2c6e780 --- /dev/null +++ b/repo-docs/RELEASE_PROCESS.md @@ -0,0 +1,95 @@ +# SalesSparrow APIs Release Process + +## Introduction + +**Objective:** Ensure that our open source backend repository is consistently and effectively updated, with full transparency to our community. Our agile approach, combined with a unique milestone branch system and continuous integration, strives to deliver high-quality updates in a structured manner. + +--- + +## **1. Pre-Release Planning** + +### **1.1 Sprint Planning** + +- At the beginning of each sprint, prioritize the GitHub issues to be addressed. +- Label issues with milestones corresponding to the anticipated release version (e.g., `v1.2.0`). +- Whenever a bug is discovered or a new feature needs to be implemented, an issue is created on GitHub. +- Issues are tagged appropriately (e.g., `bug`, `enhancement`, `documentation`) and are assigned to the relevant team members. + +### **1.2 Continuous Integration (CI)** + +- Ensure that any code pushed to the main branch passes through a CI pipeline, which includes lint checks, unit tests, integration tests, and other relevant checks. +- For each issue team members work on, they create a new branch from the `milestone` branch created for that milestone. e.g. `milestone/v1.2.0` +--- + +## **2. Release Candidate (RC)** + +### **2.1 Branching** + +- Create a release branch off the main branch named `release/vX.Y.Z` (where `X.Y.Z` is the release version) or use the `milestone` branch approach based on project conventions. +- Developers raise PRs against this branch (or the `milestone` branch if using that approach). PR titles and descriptions should be detailed, linking back to the original issue. + +### **2.2 Version Update** + +- Update the version in the project configuration or metadata files. + +### **2.3 Testing** + +- PRs will automatically trigger the CI workflow, ensuring that the combined changes from the entire milestone don't introduce any issues when integrated. +- Ensure that the release candidate is thoroughly tested. This may include: + - Regression Testing + - Performance Testing + - Security Scanning + - User Acceptance Testing (UAT) for significant features or changes + +### **2.4 Documentation** + +- Update the `CHANGELOG.md` or equivalent file with the details of the changes, enhancements, fixes, and any potential breaking changes. +- Ensure that all new features, enhancements, or changes are documented appropriately. + +--- + +## **3. Release** + +### **3.1 Merging** + +- Once the release candidate is deemed stable, merge the `release/vX.Y.Z` branch (or the `milestone` branch) into the `main` branch. + +### **3.2 GitHub Release** + +- On the GitHub repository, navigate to the "Releases" tab. +- Draft a new release, inputting the tag version, release title, and the notes from the `CHANGELOG.md`. +- Publish the release. This action will notify watchers and those who have starred the repository. + +### **3.3 Announcements** + +- Announce the new release on any relevant communication channels, such as the project's mailing list, Twitter, Discord, or other community platforms. + +--- + +## **4. Post-Release** + +### **4.1 Monitoring** + +- Monitor any feedback or issues reported by users related to the new release. + +### **4.2 Hotfixes** + +- If any critical issues or bugs are reported, prioritize and address them immediately. +- For significant bugs, create a hotfix branch off the main branch, apply the fix, and then merge it back into the main branch. +- Tag a new minor release version (e.g., `v1.2.1`) and follow the release steps as mentioned above. + +### **4.3 Sprint Retrospective** + +- At the end of the sprint or after the release, review the process and any feedback. +- Identify areas of improvement or optimization for the next release cycle. + +--- + +## **5. Best Practices** + +- **Transparency:** Ensure all decisions, issues, and discussions related to the release are public and transparent. +- **Communication:** Keep the community informed about release dates, anticipated changes, and any delays. +- **Documentation:** All features and changes should be well-documented to assist users and contributors. +- **Feedback Loop:** Encourage users to report bugs, issues, or provide feedback about the release. + +--- \ No newline at end of file diff --git a/sample.secrets.json b/sample.secrets.json new file mode 100644 index 00000000..f3be477d --- /dev/null +++ b/sample.secrets.json @@ -0,0 +1,18 @@ +{ + "ENCRYPTION_KEY": "", + "API_COOKIE_SECRET": "", + "AWS_IAM_REGION": "", + "KMS_KEY_ID": "", + "SALESFORCE_CLIENT_ID": "", + "SALESFORCE_CLIENT_SECRET": "", + "SALESFORCE_AUTH_URL": "", + "SALESFORCE_WHITELISTED_REDIRECT_URIS": "", + "MEMCACHED_CACHE_HOST": "", + "MEMCACHED_CACHE_PORT": "", + "LOG_LEVEL": "", + "DYNAMO_DB_URL": "", + "ERROR_MAIL_FROM": "", + "ERROR_MAIL_TO": "", + "COOKIE_DOMAIN": "", + "OPENAI_API_KEY": "" +} \ No newline at end of file diff --git a/set_env_vars-sample.sh b/set_env_vars-sample.sh deleted file mode 100644 index fa928f46..00000000 --- a/set_env_vars-sample.sh +++ /dev/null @@ -1,32 +0,0 @@ -# Application -export ENVIRONMENT='development' -export SALESFORCE_WHITELISTED_REDIRECT_URIS='' - -# Authentication -export ENCRYPTION_KEY='1234567890' -export API_COOKIE_SECRET='1234567890' - -# AWS -export AWS_IAM_ACCESS_KEY_ID='' -export AWS_IAM_SECRET_ACCESS_KEY='' -export AWS_IAM_REGION='' -export KMS_KEY_ID='' - -# Dynamo DB -export DYNAMO_DB_URL='http://dynamodb:8000' - -# Salesforce -export SALESFORCE_AUTH_URL='https://test.salesforce.com' -export SALESFORCE_CLIENT_ID='' -export SALESFORCE_CLIENT_SECRET='' - -# Memcached -export MEMCACHED_CACHE_HOST='localhost' -export MEMCACHED_CACHE_PORT='11211' - -# Error Email -export ERROR_MAIL_FROM='' -export ERROR_MAIL_TO='' - -# Logging -export LOG_LEVEL='info' diff --git a/set_testing_env_vars-sample.sh b/set_testing_env_vars-sample.sh deleted file mode 100644 index 929c6d4b..00000000 --- a/set_testing_env_vars-sample.sh +++ /dev/null @@ -1,32 +0,0 @@ -# Application -export ENVIRONMENT='test' -export SALESFORCE_WHITELISTED_REDIRECT_URIS='' - -# Authentication -export ENCRYPTION_KEY='1234567890' -export API_COOKIE_SECRET='1234567890' - -# AWS -export AWS_IAM_ACCESS_KEY_ID='accessKeyId' -export AWS_IAM_SECRET_ACCESS_KEY='secretAccessKey' -export AWS_IAM_REGION='region' -export KMS_KEY_ID='' - -# Database -export DYNAMO_DB_URL='http://localhost:8000' - -# Salesforce -export SALESFORCE_CLIENT_ID='' -export SALESFORCE_CLIENT_SECRET='' -export SALESFORCE_AUTH_URL='' - -# Memcached -export MEMCACHED_CACHE_HOST='localhost' -export MEMCACHED_CACHE_PORT='11211' - -# Error Email -export ERROR_MAIL_FROM='' -export ERROR_MAIL_TO='' - -# Logging -export LOG_LEVEL='info' \ No newline at end of file diff --git a/src/main/java/com/salessparrow/api/SalesSparrowApi.java b/src/main/java/com/salessparrow/api/SalesSparrowApi.java index 63981bc6..1fe1c6e5 100644 --- a/src/main/java/com/salessparrow/api/SalesSparrowApi.java +++ b/src/main/java/com/salessparrow/api/SalesSparrowApi.java @@ -2,11 +2,11 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration; import org.springframework.cache.annotation.EnableCaching; import org.springframework.scheduling.annotation.EnableAsync; - -@SpringBootApplication +@SpringBootApplication(exclude = { UserDetailsServiceAutoConfiguration.class }) @EnableCaching // This enables caching @EnableAsync // This enables asynchronous processing in Spring public class SalesSparrowApi { @@ -14,4 +14,5 @@ public class SalesSparrowApi { public static void main(String[] args) { SpringApplication.run(SalesSparrowApi.class, args); } + } diff --git a/src/main/java/com/salessparrow/api/changelogs/DatabaseChangelog.java b/src/main/java/com/salessparrow/api/changelogs/DatabaseChangelog.java index 595aa24e..2896b5dc 100644 --- a/src/main/java/com/salessparrow/api/changelogs/DatabaseChangelog.java +++ b/src/main/java/com/salessparrow/api/changelogs/DatabaseChangelog.java @@ -15,6 +15,7 @@ @ChangeLog public class DatabaseChangelog { + Logger logger = LoggerFactory.getLogger(DatabaseChangelog.class); @ChangeSet(order = "001", id = "001", author = "testAuthor") @@ -22,14 +23,10 @@ public void createSalesforceOrganizationsTable(AmazonDynamoDB db) { String tableName = DynamoDbTableNameConstants.salesforceOrganizationsTableName(); logger.info("Creating table: " + tableName); - CreateTableRequest request = new CreateTableRequest() - .withTableName(tableName) - .withAttributeDefinitions( - new AttributeDefinition("external_organization_id", - ScalarAttributeType.S)) - .withKeySchema( - new KeySchemaElement("external_organization_id", KeyType.HASH)) - .withBillingMode("PAY_PER_REQUEST"); + CreateTableRequest request = new CreateTableRequest().withTableName(tableName) + .withAttributeDefinitions(new AttributeDefinition("external_organization_id", ScalarAttributeType.S)) + .withKeySchema(new KeySchemaElement("external_organization_id", KeyType.HASH)) + .withBillingMode("PAY_PER_REQUEST"); db.createTable(request); logger.info("Done creating table: " + tableName); @@ -40,13 +37,10 @@ public void createSalesforceOAuthTokensTable(AmazonDynamoDB db) { String tableName = DynamoDbTableNameConstants.salesforceOauthTokensTableName(); logger.info("Creating table:" + tableName); - CreateTableRequest request = new CreateTableRequest() - .withTableName(tableName) - .withAttributeDefinitions( - new AttributeDefinition("external_user_id", ScalarAttributeType.S)) - .withKeySchema( - new KeySchemaElement("external_user_id", KeyType.HASH)) - .withBillingMode("PAY_PER_REQUEST"); + CreateTableRequest request = new CreateTableRequest().withTableName(tableName) + .withAttributeDefinitions(new AttributeDefinition("external_user_id", ScalarAttributeType.S)) + .withKeySchema(new KeySchemaElement("external_user_id", KeyType.HASH)) + .withBillingMode("PAY_PER_REQUEST"); db.createTable(request); @@ -58,16 +52,14 @@ public void createSalesforceUsersTable(AmazonDynamoDB db) { String tableName = DynamoDbTableNameConstants.salesforceUsersTableName(); logger.info("Creating table:" + tableName); - CreateTableRequest request = new CreateTableRequest() - .withTableName(tableName) - .withAttributeDefinitions( - new AttributeDefinition("external_user_id", ScalarAttributeType.S)) - .withKeySchema( - new KeySchemaElement("external_user_id", KeyType.HASH)) - .withBillingMode("PAY_PER_REQUEST"); + CreateTableRequest request = new CreateTableRequest().withTableName(tableName) + .withAttributeDefinitions(new AttributeDefinition("external_user_id", ScalarAttributeType.S)) + .withKeySchema(new KeySchemaElement("external_user_id", KeyType.HASH)) + .withBillingMode("PAY_PER_REQUEST"); db.createTable(request); logger.info("Done creating table: " + tableName); } + } \ No newline at end of file diff --git a/src/main/java/com/salessparrow/api/config/AwsConfig.java b/src/main/java/com/salessparrow/api/config/AwsConfig.java index 7e175e74..ba1268fd 100644 --- a/src/main/java/com/salessparrow/api/config/AwsConfig.java +++ b/src/main/java/com/salessparrow/api/config/AwsConfig.java @@ -1,7 +1,6 @@ package com.salessparrow.api.config; -import com.amazonaws.auth.AWSStaticCredentialsProvider; -import com.amazonaws.auth.BasicAWSCredentials; +import com.amazonaws.client.builder.AwsClientBuilder; import com.amazonaws.services.kms.AWSKMS; import com.amazonaws.services.kms.AWSKMSClientBuilder; @@ -10,45 +9,47 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; + /** * Configuration class for AWS-related beans and settings. - * + * */ @Configuration public class AwsConfig { - /** - * Creates and configures an AWS KMS (Key Management Service) client. - * - * @return An instance of AWSKMS that allows access to AWS KMS operations. - */ - @Bean - public AWSKMS kmsClient() { - BasicAWSCredentials awsCredentials = new BasicAWSCredentials( - CoreConstants.awsAccessKeyId(), - CoreConstants.awsSecretAccessKey()); - - return AWSKMSClientBuilder.standard() - .withCredentials(new AWSStaticCredentialsProvider(awsCredentials)) - .withRegion(CoreConstants.awsRegion()) - .build(); - } - - /** - * Creates and configures an AWS SES (Simple Email Service) client. - * - * @return An instance of AmazonSimpleEmailService that allows access to AWS SES operations. - */ - @Bean - public AmazonSimpleEmailService sesClient() { - BasicAWSCredentials awsCredentials = new BasicAWSCredentials( - CoreConstants.awsAccessKeyId(), - CoreConstants.awsSecretAccessKey()); - - return AmazonSimpleEmailServiceClientBuilder.standard() - .withCredentials(new AWSStaticCredentialsProvider(awsCredentials)) - .withRegion(CoreConstants.awsRegion()) - .build(); - } + /** + * Returns an instance of the AWS Key Management Service (KMS) client based on the + * environment. + * + * @implNote - Client is configured to use the local KMS endpoint for following + * environments - development - test - local-test For test and production + * environments, the client is configured to use the AWS KMS endpoint + * @return An instance of the AWSKMS client configured for the appropriate + * environment. + * + */ + @Bean + public AWSKMS kmsClient() { + if (CoreConstants.isDevEnvironment() || CoreConstants.isTestEnvironment() + || CoreConstants.isLocalTestEnvironment()) { + + AwsClientBuilder.EndpointConfiguration endpointConfiguration = new AwsClientBuilder.EndpointConfiguration( + CoreConstants.localKmsEndpoint(), CoreConstants.awsRegion()); + + return AWSKMSClientBuilder.standard().withEndpointConfiguration(endpointConfiguration).build(); + } + + return AWSKMSClientBuilder.standard().withRegion(CoreConstants.awsRegion()).build(); + } + + /** + * Creates and configures an AWS SES (Simple Email Service) client. + * @return An instance of AmazonSimpleEmailService that allows access to AWS SES + * operations. + */ + @Bean + public AmazonSimpleEmailService sesClient() { + return AmazonSimpleEmailServiceClientBuilder.standard().withRegion(CoreConstants.awsRegion()).build(); + } } \ No newline at end of file diff --git a/src/main/java/com/salessparrow/api/config/CoreConstants.java b/src/main/java/com/salessparrow/api/config/CoreConstants.java index b381cbe6..270174d2 100644 --- a/src/main/java/com/salessparrow/api/config/CoreConstants.java +++ b/src/main/java/com/salessparrow/api/config/CoreConstants.java @@ -1,95 +1,117 @@ package com.salessparrow.api.config; +import com.salessparrow.api.lib.globalConstants.SecretConstants; + /** * Class to get the environment variables. */ public class CoreConstants { - public static String encryptionKey() { - return System.getenv("ENCRYPTION_KEY"); - } - - public static String apiCookieSecret() { - return System.getenv("API_COOKIE_SECRET"); - } - - public static String environment() { - return System.getenv("ENVIRONMENT"); - } - - public static Boolean isDevEnvironment() { - return environment().equals("development"); - } - - public static Boolean isTestEnvironment() { - return environment().equals("test"); - } - - public static String awsAccessKeyId() { - return System.getenv("AWS_IAM_ACCESS_KEY_ID"); - } - - public static String awsSecretAccessKey() { - return System.getenv("AWS_IAM_SECRET_ACCESS_KEY"); - } - - public static String awsRegion() { - return System.getenv("AWS_IAM_REGION"); - } - - public static String kmsKeyId() { - return System.getenv("KMS_KEY_ID"); - } - - public static String salesforceAuthUrl() { - return System.getenv("SALESFORCE_AUTH_URL"); - } - - public static String salesforceClientId() { - return System.getenv("SALESFORCE_CLIENT_ID"); - } - - public static String salesforceClientSecret() { - return System.getenv("SALESFORCE_CLIENT_SECRET"); - } - - /** - * This method returns the memcached address that is going to be used for locals - * - * @return String - */ - public static String memcachedAddress() { - return System.getenv("MEMCACHED_CACHE_HOST") + ":" + System.getenv("MEMCACHED_CACHE_PORT"); - } - - /** - * This method returns the list of redirect URIs that are whitelisted in - * Salesforce connected app for oAuth. - * - * @return String[] - */ - public static String[] getWhitelistedRedirectUris() { - String redirectUrisJson = System.getenv("SALESFORCE_WHITELISTED_REDIRECT_URIS"); - return redirectUrisJson.split(","); - } - - /** - * This method returns the email address that will be used to send error emails. - * This email address or its domain must be verified in AWS SES. - * - * @return String - */ - public static String errorEmailFrom() { - return System.getenv("ERROR_MAIL_FROM"); - } - - /** - * This method returns the email address that will receive the error emails. - * - * @return String - */ - public static String errorEmailTo() { - return System.getenv("ERROR_MAIL_TO"); - } + /* Start: Env variables required before spring application context is initialized */ + + public static String environment() { + return System.getenv("ENVIRONMENT"); + } + + public static Boolean isDevEnvironment() { + return environment().equals("development"); + } + + public static Boolean isTestEnvironment() { + return environment().equals("test"); + } + + public static Boolean isLocalTestEnvironment() { + return environment().equals("local-test"); + } + + /* End: Env variables required before spring application context is initialized */ + + public static String cookieDomain() { + return SecretConstants.cookieDomain(); + } + + public static String awsRegion() { + return SecretConstants.awsRegion(); + } + + public static String encryptionKey() { + return SecretConstants.encryptionKey(); + } + + public static String apiCookieSecret() { + return SecretConstants.apiCookieSecret(); + } + + public static String kmsKeyId() { + return SecretConstants.kmsKeyId(); + } + + public static String salesforceAuthUrl() { + return SecretConstants.salesforceAuthUrl(); + } + + public static String salesforceClientId() { + return SecretConstants.salesforceClientId(); + } + + public static String salesforceClientSecret() { + return SecretConstants.salesforceClientSecret(); + } + + public static String localKmsEndpoint() { + return SecretConstants.localKmsEndpoint(); + } + + /** + * This method returns the memcached address that is going to be used for locals + * @return String + */ + public static String memcachedAddress() { + return SecretConstants.memcachedHost() + ":" + SecretConstants.memcachedPort(); + } + + /** + * This method returns the list of redirect URIs that are whitelisted in Salesforce + * connected app for oAuth. + * @return String[] + */ + public static String[] getWhitelistedRedirectUris() { + String redirectUrisJson = SecretConstants.salesforceWhitelistedRedirectUris(); + return redirectUrisJson.split(","); + } + + /** + * This method returns api key for OpenAI. + * @return String + */ + public static String openAiApiKey() { + return SecretConstants.openAiApiKey(); + } + + /** + * This method returns the email address that will be used to send error emails. This + * email address or its domain must be verified in AWS SES. + * @return String + */ + public static String errorEmailFrom() { + return SecretConstants.errorEmailFrom(); + } + + /** + * This method returns the email address that will receive the error emails. + * @return String + */ + public static String errorEmailTo() { + return SecretConstants.errorEmailTo(); + } + + /** + * This method returns the dynamodb url. + * @return + */ + public static String dynamoDbUrl() { + return SecretConstants.dynamoDbUrl(); + } } diff --git a/src/main/java/com/salessparrow/api/config/CorsConfig.java b/src/main/java/com/salessparrow/api/config/CorsConfig.java index 64b4a8d9..d378d8a2 100644 --- a/src/main/java/com/salessparrow/api/config/CorsConfig.java +++ b/src/main/java/com/salessparrow/api/config/CorsConfig.java @@ -1,4 +1,5 @@ package com.salessparrow.api.config; + import org.springframework.context.annotation.Profile; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @@ -13,15 +14,16 @@ @Profile("!production") public class CorsConfig implements WebMvcConfigurer { - @Override - public void addCorsMappings(CorsRegistry registry) { - Logger logger = LoggerFactory.getLogger(CorsConfig.class); - logger.info("Add Cors config for localhost:3000"); + @Override + public void addCorsMappings(CorsRegistry registry) { + Logger logger = LoggerFactory.getLogger(CorsConfig.class); + logger.info("Add Cors config for localhost:3000"); + + registry.addMapping("/**") + .allowedOrigins("http://localhost:3000") + .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") + .allowCredentials(true) + .maxAge(3600); + } - registry.addMapping("/**") - .allowedOrigins("http://localhost:3000") - .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") - .allowCredentials(true) - .maxAge(3600); - } } diff --git a/src/main/java/com/salessparrow/api/config/DynamoBeeConfig.java b/src/main/java/com/salessparrow/api/config/DynamoBeeConfig.java index b11738e9..e1eff2aa 100644 --- a/src/main/java/com/salessparrow/api/config/DynamoBeeConfig.java +++ b/src/main/java/com/salessparrow/api/config/DynamoBeeConfig.java @@ -13,16 +13,19 @@ */ @Configuration public class DynamoBeeConfig { - @Autowired - private AmazonDynamoDB db; - @Autowired - private dynamoBeeConfigConstants dynamoBeeConfigConstants; + @Autowired + private AmazonDynamoDB db; + + @Autowired + private dynamoBeeConfigConstants dynamoBeeConfigConstants; + + @Bean + public Dynamobee dynamobee() { + Dynamobee runner = new Dynamobee(db); + runner.setChangeLogsScanPackage(dynamoBeeConfigConstants.getChangeLogScanPackage()) + .setChangelogTableName(dynamoBeeConfigConstants.getChangelogTableName()); + return runner; + } - @Bean - public Dynamobee dynamobee(){ - Dynamobee runner = new Dynamobee(db); - runner.setChangeLogsScanPackage(dynamoBeeConfigConstants.getChangeLogScanPackage()).setChangelogTableName(dynamoBeeConfigConstants.getChangelogTableName()); - return runner; - } } diff --git a/src/main/java/com/salessparrow/api/config/DynamoDBConfiguration.java b/src/main/java/com/salessparrow/api/config/DynamoDBConfiguration.java index 16052285..c0951f8c 100644 --- a/src/main/java/com/salessparrow/api/config/DynamoDBConfiguration.java +++ b/src/main/java/com/salessparrow/api/config/DynamoDBConfiguration.java @@ -1,77 +1,96 @@ package com.salessparrow.api.config; -import com.amazonaws.auth.AWSStaticCredentialsProvider; -import com.amazonaws.auth.BasicAWSCredentials; import com.amazonaws.client.builder.AwsClientBuilder; import com.amazonaws.services.dynamodbv2.AmazonDynamoDB; import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapperConfig; +import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapperConfig.SaveBehavior; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class DynamoDBConfiguration { - Logger logger = LoggerFactory.getLogger(DynamoDBConfiguration.class); - - - @Value("${aws.dynamodb.endpoint}") - private String dynamodbEndpoint; - - @Value("${aws.region}") - private String awsRegion; - - @Value("${aws.dynamodb.accessKey}") - private String dynamodbAccessKey; - - @Value("${aws.dynamodb.secretKey}") - private String dynamodbSecretKey; - - - @Bean - public DynamoDBMapper dynamoDBMapper() { - DynamoDBMapper defaultMapper = new DynamoDBMapper(buildAmazonDynamoDB(), dynamoDBMapperConfig()); - - //Override DynamoDb operations to add logging. - return new DynamoDBMapper(buildAmazonDynamoDB(), dynamoDBMapperConfig()) { - - @Override - public T load(Class clazz, Object hashKey) { - logger.debug("DBQuery:Load: table-{} hashKey-{}", clazz.getSimpleName(), hashKey); - return defaultMapper.load(clazz, hashKey); - } - - @Override - public void save(T object) { - logger.debug("DBQuery:Save: table-{}", object.getClass().getSimpleName()); - defaultMapper.save(object); - } - // Similarly, you can override other used methods like delete, batchSave, etc. similarly - }; - } - - @Bean - public AmazonDynamoDB buildAmazonDynamoDB() { - return AmazonDynamoDBClientBuilder - .standard() - .withEndpointConfiguration( - new AwsClientBuilder.EndpointConfiguration(dynamodbEndpoint,awsRegion)) - .withCredentials(new AWSStaticCredentialsProvider( - new BasicAWSCredentials(dynamodbAccessKey,dynamodbSecretKey))) - .build(); - } - - @Bean - DynamoDBMapperConfig dynamoDBMapperConfig() { - String prefix = CoreConstants.environment() + "_"; - return new DynamoDBMapperConfig.Builder() - .withTableNameOverride(DynamoDBMapperConfig.TableNameOverride.withTableNamePrefix(prefix)) - .build(); - } -} - + Logger logger = LoggerFactory.getLogger(DynamoDBConfiguration.class); + + @Bean + public DynamoDBMapper dynamoDBMapper() { + DynamoDBMapper defaultMapper = new DynamoDBMapper(buildAmazonDynamoDB(), dynamoDBMapperConfig()); + // Override DynamoDb operations to add logging. + return new DynamoDBMapper(buildAmazonDynamoDB(), dynamoDBMapperConfig()) { + @Override + public T load(Class clazz, Object hashKey) { + T response = null; + long startTimestamp = System.currentTimeMillis(); + try { + response = defaultMapper.load(clazz, hashKey); + } + catch (Exception e) { + logger.debug("DBQuery:Load: table-{} hashKey-{}", clazz.getSimpleName(), hashKey); + logger.error("DBQuery:Load: exception-{}", e); + throw new RuntimeException("Error during load database operation", e); + } + + long duration = System.currentTimeMillis() - startTimestamp; + logger.debug("({} ms)DBQuery:Load: table-{} hashKey-{}", duration, clazz.getSimpleName(), hashKey); + return response; + } + + @Override + public void save(T object) { + long startTimestamp = System.currentTimeMillis(); + try { + defaultMapper.save(object); + } + catch (Exception e) { + logger.debug("DBQuery:Save: table-{}", object.getClass().getSimpleName()); + logger.error("DBQuery:Save: exception-{}", e); + throw new RuntimeException("Error during save database operation", e); + } + + long duration = System.currentTimeMillis() - startTimestamp; + logger.debug("({} ms)DBQuery:Save: table-{}", duration, object.getClass().getSimpleName()); + } + + @Override + public void save(T object, DynamoDBMapperConfig config) { + long startTimestamp = System.currentTimeMillis(); + try { + defaultMapper.save(object, config); + } + catch (Exception e) { + logger.debug("DBQuery:Save: table-{}", object.getClass().getSimpleName()); + logger.error("DBQuery:Save: exception-{}", e); + throw new RuntimeException("Error during save database operation", e); + } + + long duration = System.currentTimeMillis() - startTimestamp; + logger.debug("({} ms)DBQuery:Save: table-{}", duration, object.getClass().getSimpleName()); + } + + // Similarly, you can override other used methods like delete, batchSave, etc. + }; + } + + @Bean + public AmazonDynamoDB buildAmazonDynamoDB() { + return AmazonDynamoDBClientBuilder.standard() + .withEndpointConfiguration( + new AwsClientBuilder.EndpointConfiguration(CoreConstants.dynamoDbUrl(), CoreConstants.awsRegion())) + .build(); + } + + @Bean + DynamoDBMapperConfig dynamoDBMapperConfig() { + String prefix = CoreConstants.environment() + "_"; + return new DynamoDBMapperConfig.Builder() + .withTableNameOverride(DynamoDBMapperConfig.TableNameOverride.withTableNamePrefix(prefix)) + .withSaveBehavior(SaveBehavior.UPDATE_SKIP_NULL_ATTRIBUTES) + .build(); + } + +} \ No newline at end of file diff --git a/src/main/java/com/salessparrow/api/config/FilterConfig.java b/src/main/java/com/salessparrow/api/config/FilterConfig.java new file mode 100644 index 00000000..8ba64fc8 --- /dev/null +++ b/src/main/java/com/salessparrow/api/config/FilterConfig.java @@ -0,0 +1,39 @@ +package com.salessparrow.api.config; + +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import com.salessparrow.api.filter.SameSiteCookieFilter; +import com.salessparrow.api.filter.SanitizationFilter; + +@Configuration +public class FilterConfig { + + /** + * Register SanitizationFilter + * @return FilterRegistrationBean + */ + @Bean + public FilterRegistrationBean sanitizationFilter() { + FilterRegistrationBean registrationBean = new FilterRegistrationBean<>(); + registrationBean.setFilter(new SanitizationFilter()); + registrationBean.addUrlPatterns("/*"); + registrationBean.setOrder(1); + return registrationBean; + } + + /** + * Register SameSiteCookieFilter + * @return FilterRegistrationBean + */ + @Bean + public FilterRegistrationBean sameSiteCookieFilter() { + FilterRegistrationBean registrationBean = new FilterRegistrationBean<>(); + registrationBean.setFilter(new SameSiteCookieFilter()); + registrationBean.addUrlPatterns("/*"); // or specific URL patterns + registrationBean.setOrder(2); + return registrationBean; + } + +} diff --git a/src/main/java/com/salessparrow/api/config/InterceptorConfig.java b/src/main/java/com/salessparrow/api/config/InterceptorConfig.java index c82c0026..a0e939ea 100644 --- a/src/main/java/com/salessparrow/api/config/InterceptorConfig.java +++ b/src/main/java/com/salessparrow/api/config/InterceptorConfig.java @@ -6,37 +6,49 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import com.salessparrow.api.interceptors.LoggerInterceptor; +import com.salessparrow.api.interceptors.JsonOnlyInterceptor; import com.salessparrow.api.interceptors.UserAuthInterceptor; import com.salessparrow.api.interceptors.V1Interceptor; @Configuration public class InterceptorConfig implements WebMvcConfigurer { - - @Autowired - private final LoggerInterceptor loggerInterceptor; - - @Autowired - private final V1Interceptor V1Interceptor; - - @Autowired - private final UserAuthInterceptor userAuthInterceptor; - - public InterceptorConfig(UserAuthInterceptor userAuthInterceptor, LoggerInterceptor loggerInterceptor, V1Interceptor V1Interceptor) { - this.userAuthInterceptor = userAuthInterceptor; - this.loggerInterceptor = loggerInterceptor; - this.V1Interceptor = V1Interceptor; - } - - @Override - public void addInterceptors(InterceptorRegistry registry) { - registry.addInterceptor(loggerInterceptor) - .addPathPatterns("/**"); - - registry.addInterceptor(V1Interceptor) - .addPathPatterns("/api/v1/**"); - - registry.addInterceptor(userAuthInterceptor) - .addPathPatterns("/**") - .excludePathPatterns("/api/v1/auth/salesforce/**"); - } + + @Autowired + private final LoggerInterceptor loggerInterceptor; + + @Autowired + private final JsonOnlyInterceptor jsonOnlyInterceptor; + + @Autowired + private final V1Interceptor V1Interceptor; + + @Autowired + private final UserAuthInterceptor userAuthInterceptor; + + public InterceptorConfig(UserAuthInterceptor userAuthInterceptor, JsonOnlyInterceptor jsonOnlyInterceptor, + LoggerInterceptor loggerInterceptor, V1Interceptor V1Interceptor) { + this.userAuthInterceptor = userAuthInterceptor; + this.loggerInterceptor = loggerInterceptor; + this.V1Interceptor = V1Interceptor; + this.jsonOnlyInterceptor = jsonOnlyInterceptor; + } + + @Override + public void addInterceptors(InterceptorRegistry registry) { + /* Add logger interceptor to all the routes */ + registry.addInterceptor(loggerInterceptor).addPathPatterns("/**"); + + /* Add json only interceptor to all the routes */ + registry.addInterceptor(jsonOnlyInterceptor).addPathPatterns("/**"); + + /* Add v1 interceptor only to all the routes */ + registry.addInterceptor(V1Interceptor).addPathPatterns("/api/v1/**"); + + /* Add user auth interceptor to all the routes except the ones below */ + registry.addInterceptor(userAuthInterceptor) + .addPathPatterns("/**") + .excludePathPatterns("/api/v1/auth/salesforce/**") + .excludePathPatterns("/api/v1/health-check"); + } + } \ No newline at end of file diff --git a/src/main/java/com/salessparrow/api/config/MemcachedConfig.java b/src/main/java/com/salessparrow/api/config/MemcachedConfig.java index 1dc3738a..6d0e6ce1 100644 --- a/src/main/java/com/salessparrow/api/config/MemcachedConfig.java +++ b/src/main/java/com/salessparrow/api/config/MemcachedConfig.java @@ -7,6 +7,7 @@ import com.salessparrow.api.exception.CustomException; import com.salessparrow.api.lib.errorLib.ErrorObject; import com.salessparrow.api.lib.globalConstants.CacheConstants; +import com.salessparrow.api.utility.CacheKeyGenerator; import com.salessparrow.api.utility.Memcached; import net.spy.memcached.AddrUtil; @@ -22,7 +23,6 @@ import org.springframework.cache.interceptor.CacheResolver; import org.springframework.cache.interceptor.KeyGenerator; import org.springframework.cache.interceptor.SimpleCacheErrorHandler; -import org.springframework.cache.interceptor.SimpleKeyGenerator; import org.springframework.cache.support.SimpleCacheManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -33,94 +33,77 @@ @Configuration public class MemcachedConfig implements CachingConfigurer { - private MemcachedClient cache; - - private static final Logger logger = LoggerFactory.getLogger(MemcachedConfig.class); - - /** - * Cache Manager Bean to initialize the cache client. - * - * @return CacheManager - */ - @Override - @Bean - public CacheManager cacheManager() { - SimpleCacheManager cacheManager = new SimpleCacheManager(); - - setMemcachedClient(); - - cacheManager.setCaches(internalCaches(this.cache)); - return cacheManager; - } - - /** - * Internal Caches - * All caches needs to be added here along with their expiry time. - * - * @return Collection - */ - private Collection internalCaches(MemcachedClient cache) { - final Collection caches = new ArrayList<>(); - - caches.add(new Memcached( - CacheConstants.SS_SALESFORCE_USER_CACHE, - CacheConstants.SS_SALESFORCE_USER_CACHE_EXP, - cache - )); - caches.add( - new Memcached( - CacheConstants.SS_SALESFORCE_OAUTH_TOKEN_CACHE, - CacheConstants.SS_SALESFORCE_OAUTH_TOKEN_CACHE_EXP, - cache - )); - return caches; - } - - public void setMemcachedClient() { - logger.info("Memcached Client Initialized"); - try { - this.cache = new MemcachedClient( - new ConnectionFactoryBuilder() - .setTranscoder(new SerializingTranscoder()) - .setProtocol(ConnectionFactoryBuilder.Protocol.BINARY) - .build(), - AddrUtil.getAddresses(CoreConstants.memcachedAddress())); - } catch (Exception e) { - throw new CustomException( - new ErrorObject( - "a_c_mc_mc_1", - "something_went_wrong", - e.getMessage())); - } - } - - /** - * Key Generator - * - * @return KeyGenerator - */ - @Override - public KeyGenerator keyGenerator() { - return new SimpleKeyGenerator(); - } - - /** - * Error Handler - * - * @return CacheErrorHandler - */ - @Override - public CacheErrorHandler errorHandler() { - return new SimpleCacheErrorHandler(); - } - - /** - * Cache Resolver - * - * @return CacheResolver - */ - @Override - public CacheResolver cacheResolver() { - return null; - } + private MemcachedClient cache; + + private static final Logger logger = LoggerFactory.getLogger(MemcachedConfig.class); + + /** + * Cache Manager Bean to initialize the cache client. + * @return CacheManager + */ + @Override + @Bean + public CacheManager cacheManager() { + SimpleCacheManager cacheManager = new SimpleCacheManager(); + + setMemcachedClient(); + + cacheManager.setCaches(internalCaches(this.cache)); + return cacheManager; + } + + /** + * Internal Caches All caches needs to be added here along with their expiry time. + * @return Collection + */ + private Collection internalCaches(MemcachedClient cache) { + final Collection caches = new ArrayList<>(); + + caches.add(new Memcached(CacheConstants.SS_SALESFORCE_USER_CACHE, CacheConstants.SS_SALESFORCE_USER_CACHE_EXP, + cache)); + caches.add(new Memcached(CacheConstants.SS_SALESFORCE_OAUTH_TOKEN_CACHE, + CacheConstants.SS_SALESFORCE_OAUTH_TOKEN_CACHE_EXP, cache)); + return caches; + } + + public void setMemcachedClient() { + logger.info("Memcached Client Initialized"); + try { + this.cache = new MemcachedClient(new ConnectionFactoryBuilder().setTranscoder(new SerializingTranscoder()) + .setProtocol(ConnectionFactoryBuilder.Protocol.BINARY) + .build(), AddrUtil.getAddresses(CoreConstants.memcachedAddress())); + } + catch (Exception e) { + throw new CustomException(new ErrorObject("a_c_mc_mc_1", "something_went_wrong", e.getMessage())); + } + } + + /** + * Key Generator is overriden to include a prefix that can be different for different + * environments. + * @return KeyGenerator + */ + @Override + public KeyGenerator keyGenerator() { + return new CacheKeyGenerator(); + } + + /** + * Error Handler + * @return CacheErrorHandler + */ + @Override + public CacheErrorHandler errorHandler() { + return new SimpleCacheErrorHandler(); + } + + /** + * Cache Resolver + * @return CacheResolver + */ + @Override + public CacheResolver cacheResolver() { + return null; + } + } \ No newline at end of file diff --git a/src/main/java/com/salessparrow/api/config/SecurityConfig.java b/src/main/java/com/salessparrow/api/config/SecurityConfig.java new file mode 100644 index 00000000..d8d55c72 --- /dev/null +++ b/src/main/java/com/salessparrow/api/config/SecurityConfig.java @@ -0,0 +1,80 @@ +package com.salessparrow.api.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.header.writers.XXssProtectionHeaderWriter; +import org.springframework.security.web.header.writers.ReferrerPolicyHeaderWriter.ReferrerPolicy; + +@Configuration +@EnableWebSecurity +public class SecurityConfig { + + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + + http + // disable authorization for all routes + .authorizeHttpRequests(authorizeRequests -> authorizeRequests.anyRequest().permitAll()) + // Disable authentication for all routes + .httpBasic(httpBasic -> httpBasic.disable()) + // Disable form login + .formLogin(formLogin -> formLogin.disable()) + // Remove csrf in app routes + .csrf((csrf) -> csrf.ignoringRequestMatchers("/api/v1/**")) + + /** + * Cache-Control header - applied by default in spring security The + * Cache-Control header is the most important header to set as it effectively + * disables caching on the client side. + * + * Pragma header - applied by default in spring security The Pragma directive + * is an older directive meant for HTTP/1.0 clients where the Cache-Control + * header wasn't defined. + * + * Expires header - applied by default in spring security The Expires header + * is another older way to prevent caching, especially in HTTP/1.0. + * + * X-Content-Type-Options header - applied by default in spring security The + * X-Content-Type-Options header is a security feature that prevents pages + * from loading when they detect incorrect MIME types. By setting the value to + * "nosniff", you're instructing the browser not to override the provided + * Content-Type + * + * HSTS header The Strict-Transport-Security header is a security feature + * implemented by web browsers to ensure that websites are only accessed using + * HTTPS. + * + * X-Frame-Options header The X-Frame-Options header is a security feature + * that prevents your web page from being put in a frame. + * + * X-XSS-Protection header - applied by default in spring security The + * X-XSS-Protection header is a security feature that prevents pages from + * loading when they detect reflected cross-site scripting (XSS) attacks. + * + * It is not needed for rest api's as they are not rendered in the browser. + * But it is no harm and is a good practice to have it.The value "1; + * mode=block" instructs the browser to block the response if it detects an + * attack. + * + * Referrer-Policy header The Referrer-Policy header is a security feature + * that prevents pages from leaking information about the user's browsing + * behavior.The value "same-origin" instructs the browser to send the referrer + * header only when the request is originating from the same origin as the + * target resource. + */ + .headers(headers -> headers.frameOptions(frameOptions -> frameOptions.deny()) + .httpStrictTransportSecurity( + hsts -> hsts.includeSubDomains(true).preload(true).maxAgeInSeconds(31536000)) + .xssProtection(xss -> xss.headerValue(XXssProtectionHeaderWriter.HeaderValue.ENABLED_MODE_BLOCK)) + .referrerPolicy(referrer -> referrer.policy(ReferrerPolicy.SAME_ORIGIN))); + + // http redirect to https is handled by the reverse proxy server (nginx). So no + // need to handle it here. + + return http.build(); + } + +} diff --git a/src/main/java/com/salessparrow/api/controllers/AccountController.java b/src/main/java/com/salessparrow/api/controllers/AccountController.java index 44058227..9bce08a7 100644 --- a/src/main/java/com/salessparrow/api/controllers/AccountController.java +++ b/src/main/java/com/salessparrow/api/controllers/AccountController.java @@ -6,22 +6,15 @@ import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ModelAttribute; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import com.salessparrow.api.dto.formatter.CreateNoteFormatterDto; -import com.salessparrow.api.dto.formatter.GetAccountsFormatterDto; -import com.salessparrow.api.dto.formatter.GetNoteDetailsFormatterDto; -import com.salessparrow.api.dto.formatter.GetNotesListFormatterDto; import com.salessparrow.api.dto.requestMapper.GetAccountsDto; -import com.salessparrow.api.dto.requestMapper.NoteDto; -import com.salessparrow.api.services.accounts.CreateNoteService; +import com.salessparrow.api.dto.requestMapper.GetAccountsFeedDto; +import com.salessparrow.api.dto.responseMapper.GetAccountListResponseDto; +import com.salessparrow.api.dto.responseMapper.GetAccountsFeedResponseDto; import com.salessparrow.api.services.accounts.GetAccountListService; -import com.salessparrow.api.services.accounts.GetNoteDetailsService; -import com.salessparrow.api.services.accounts.GetNotesListService; +import com.salessparrow.api.services.accounts.GetAccountsFeedService; import jakarta.servlet.http.HttpServletRequest; import jakarta.validation.Valid; @@ -31,59 +24,33 @@ @Validated public class AccountController { - private Logger logger = org.slf4j.LoggerFactory.getLogger(AccountController.class); + private Logger logger = org.slf4j.LoggerFactory.getLogger(AccountController.class); - @Autowired - private GetAccountListService getAccountListService; + @Autowired + private GetAccountListService getAccountListService; - @Autowired - private GetNotesListService getNotesListService; + @Autowired + private GetAccountsFeedService getAccountsFeedService; - @Autowired - private GetNoteDetailsService getNoteDetailsService; + @GetMapping("") + public ResponseEntity getAccounts(HttpServletRequest request, + @Valid @ModelAttribute GetAccountsDto getAccountsDto) { + logger.info("Request received"); - @Autowired - private CreateNoteService createNoteService; + GetAccountListResponseDto getAccountsResponse = getAccountListService.getAccounts(request, getAccountsDto); - @PostMapping("/{account_id}/notes") - public ResponseEntity addNoteToAccount( - HttpServletRequest request, - @PathVariable("account_id") String accountId, - @Valid @RequestBody NoteDto note - ) { - CreateNoteFormatterDto createNoteFormatterDto = createNoteService.createNote(request, accountId, note); + return ResponseEntity.ok().body(getAccountsResponse); + } - return ResponseEntity.ok().body(createNoteFormatterDto); - } + @GetMapping("/feed") + public ResponseEntity getFeed(HttpServletRequest request, + @Valid @ModelAttribute GetAccountsFeedDto getAccountsFeedDto) { + logger.info("Request received"); - @GetMapping("") - public ResponseEntity getAccounts( - HttpServletRequest request, - @Valid @ModelAttribute GetAccountsDto getAccountsDto) { - logger.info("Request received"); + GetAccountsFeedResponseDto getAccountsFeedResponse = getAccountsFeedService.getAccountsFeed(request, + getAccountsFeedDto); - GetAccountsFormatterDto getAccountsResponse = getAccountListService.getAccounts(request, getAccountsDto); + return ResponseEntity.ok().body(getAccountsFeedResponse); + } - return ResponseEntity.ok().body(getAccountsResponse); - } - - @GetMapping("/{account_id}/notes") - public ResponseEntity getNotesList(HttpServletRequest request,@PathVariable("account_id") String accountId) { - - GetNotesListFormatterDto getNotesListResponse = getNotesListService.getNotesList(request, accountId); - - return ResponseEntity.ok().body(getNotesListResponse); - } - - @GetMapping("/{account_id}/notes/{note_id}") - public ResponseEntity getNoteFromAccount( - HttpServletRequest request, - @PathVariable("account_id") String accountId, - @PathVariable("note_id") String noteId - ) { - - GetNoteDetailsFormatterDto getNoteDetailsResponse = getNoteDetailsService.getNoteDetails(request, noteId); - - return ResponseEntity.ok().body(getNoteDetailsResponse); - } } diff --git a/src/main/java/com/salessparrow/api/controllers/AccountNoteController.java b/src/main/java/com/salessparrow/api/controllers/AccountNoteController.java new file mode 100644 index 00000000..ff6d4d6c --- /dev/null +++ b/src/main/java/com/salessparrow/api/controllers/AccountNoteController.java @@ -0,0 +1,86 @@ +package com.salessparrow.api.controllers; + +import org.slf4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.salessparrow.api.dto.formatter.CreateNoteFormatterDto; +import com.salessparrow.api.dto.formatter.GetNoteDetailsFormatterDto; +import com.salessparrow.api.dto.formatter.GetNotesListFormatterDto; +import com.salessparrow.api.dto.requestMapper.NoteDto; +import com.salessparrow.api.services.accountNotes.CreateAccountNoteService; +import com.salessparrow.api.services.accountNotes.DeleteAccountNoteService; +import com.salessparrow.api.services.accountNotes.GetAccountNoteDetailsService; +import com.salessparrow.api.services.accountNotes.GetAccountNotesListService; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.Valid; + +@RestController +@RequestMapping("/api/v1/accounts/{account_id}/notes") +@Validated +public class AccountNoteController { + + private Logger logger = org.slf4j.LoggerFactory.getLogger(AccountNoteController.class); + + @Autowired + private GetAccountNotesListService getNotesListService; + + @Autowired + private GetAccountNoteDetailsService getNoteDetailsService; + + @Autowired + private CreateAccountNoteService createNoteService; + + @Autowired + private DeleteAccountNoteService deleteAccountNoteService; + + @PostMapping("") + public ResponseEntity addNoteToAccount(HttpServletRequest request, + @PathVariable("account_id") String accountId, @Valid @RequestBody NoteDto note) { + logger.info("Create Note request received"); + + CreateNoteFormatterDto createNoteFormatterDto = createNoteService.createNote(request, accountId, note); + + return ResponseEntity.ok().body(createNoteFormatterDto); + } + + @GetMapping("") + public ResponseEntity getNotesList(HttpServletRequest request, + @PathVariable("account_id") String accountId) { + logger.info("Get Note List request received"); + + GetNotesListFormatterDto getNotesListResponse = getNotesListService.getNotesList(request, accountId); + + return ResponseEntity.ok().body(getNotesListResponse); + } + + @GetMapping("/{note_id}") + public ResponseEntity getNoteFromAccount(HttpServletRequest request, + @PathVariable("account_id") String accountId, @PathVariable("note_id") String noteId) { + logger.info("Get Note request received"); + + GetNoteDetailsFormatterDto getNoteDetailsResponse = getNoteDetailsService.getNoteDetails(request, noteId); + + return ResponseEntity.ok().body(getNoteDetailsResponse); + } + + @DeleteMapping("/{note_id}") + public ResponseEntity deleteNote(HttpServletRequest request, + @PathVariable("account_id") String accountId, @PathVariable("note_id") String noteId) { + logger.info("Delete Note request received"); + + deleteAccountNoteService.deleteAccountNote(request, accountId, noteId); + + return ResponseEntity.noContent().build(); + } + +} diff --git a/src/main/java/com/salessparrow/api/controllers/AccountTaskController.java b/src/main/java/com/salessparrow/api/controllers/AccountTaskController.java new file mode 100644 index 00000000..7cab58dc --- /dev/null +++ b/src/main/java/com/salessparrow/api/controllers/AccountTaskController.java @@ -0,0 +1,73 @@ +package com.salessparrow.api.controllers; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.salessparrow.api.dto.formatter.CreateTaskFormatterDto; +import com.salessparrow.api.dto.requestMapper.CreateAccountTaskDto; +import com.salessparrow.api.services.accountTask.CreateTaskService; +import com.salessparrow.api.services.accountTask.DeleteTaskService; +import com.salessparrow.api.dto.formatter.GetTasksListFormatterDto; +import com.salessparrow.api.services.accountTask.GetAccountTasksListService; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.Valid; + +@RestController +@RequestMapping("/api/v1/accounts") +@Validated +public class AccountTaskController { + + Logger logger = LoggerFactory.getLogger(AccountTaskController.class); + + @Autowired + private CreateTaskService createTaskService; + + @Autowired + private DeleteTaskService deleteTaskService; + + @Autowired + private GetAccountTasksListService getAccountTasksListService; + + @PostMapping("/{account_id}/tasks") + public ResponseEntity createTask(HttpServletRequest request, + @PathVariable("account_id") String accountId, @Valid @RequestBody CreateAccountTaskDto task) { + logger.info("Create task request received"); + + CreateTaskFormatterDto createTaskFormatterDto = createTaskService.createAccountTask(request, accountId, task); + + return ResponseEntity.status(HttpStatus.CREATED).body(createTaskFormatterDto); + } + + @GetMapping("/{account_id}/tasks") + public ResponseEntity getTasksList(HttpServletRequest request, + @PathVariable("account_id") String accountId) { + logger.info("Get tasks list request received"); + + GetTasksListFormatterDto getTasksListFormatterDto = getAccountTasksListService.getAccountTasksList(request, + accountId); + return ResponseEntity.status(HttpStatus.OK).body(getTasksListFormatterDto); + } + + @DeleteMapping("/{account_id}/tasks/{task_id}") + public ResponseEntity deleteTask(HttpServletRequest request, @PathVariable("account_id") String accountId, + @PathVariable("task_id") String taskId) { + logger.info("Delete task request received"); + + deleteTaskService.deleteAccountTask(request, accountId, taskId); + + return ResponseEntity.status(HttpStatus.NO_CONTENT).build(); + } + +} diff --git a/src/main/java/com/salessparrow/api/controllers/AuthController.java b/src/main/java/com/salessparrow/api/controllers/AuthController.java index f533719b..0056ac1b 100644 --- a/src/main/java/com/salessparrow/api/controllers/AuthController.java +++ b/src/main/java/com/salessparrow/api/controllers/AuthController.java @@ -19,6 +19,7 @@ import com.salessparrow.api.dto.requestMapper.SalesforceConnectDto; import com.salessparrow.api.dto.requestMapper.SalesforceRedirectUrlDto; import com.salessparrow.api.lib.CookieHelper; +import com.salessparrow.api.services.auth.DisconnectUserService; import com.salessparrow.api.services.salesforce.AuthService; import com.salessparrow.api.services.salesforce.AuthService.AuthServiceDto; @@ -30,49 +31,68 @@ @Validated public class AuthController { - Logger logger = LoggerFactory.getLogger(AuthController.class); + Logger logger = LoggerFactory.getLogger(AuthController.class); - @Autowired - private RedirectUrlService redirectUrlService; + @Autowired + private RedirectUrlService redirectUrlService; - @Autowired - private AuthService authService; + @Autowired + private AuthService authService; - @Autowired - private CookieHelper cookieHelper; + @Autowired + private DisconnectUserService disconnectUserService; - @GetMapping("/salesforce/redirect-url") - public ResponseEntity getSalesforceRedirectUrl( - @Valid @ModelAttribute SalesforceRedirectUrlDto salesforceRedirectUrlDto) { - - RedirectUrlFormatterDto redirectUrlFormatterDto = redirectUrlService.getSalesforceOauthUrl(salesforceRedirectUrlDto); + @Autowired + private CookieHelper cookieHelper; - return ResponseEntity.ok().body(redirectUrlFormatterDto); - } + @GetMapping("/salesforce/redirect-url") + public ResponseEntity getSalesforceRedirectUrl( + @Valid @ModelAttribute SalesforceRedirectUrlDto salesforceRedirectUrlDto) { - @PostMapping("/salesforce/connect") - public ResponseEntity connectToSalesforce( - @Valid @RequestBody SalesforceConnectDto salesforceConnectDto) { - logger.info("Salesforce connection request received"); + RedirectUrlFormatterDto redirectUrlFormatterDto = redirectUrlService + .getSalesforceOauthUrl(salesforceRedirectUrlDto); - AuthServiceDto authServiceResponse = authService.connectToSalesforce(salesforceConnectDto); + return ResponseEntity.ok().body(redirectUrlFormatterDto); + } - HttpHeaders headers = new HttpHeaders(); - headers = cookieHelper.setUserCookie(authServiceResponse.getCurrentUserLoginCookie(), headers); + @PostMapping("/salesforce/connect") + public ResponseEntity connectToSalesforce(HttpServletRequest request, + @Valid @RequestBody SalesforceConnectDto salesforceConnectDto) { + logger.info("Salesforce connection request received"); - SalesforceConnectFormatterDto salesforceConnectResponse = new SalesforceConnectFormatterDto(); - salesforceConnectResponse.setCurrentUser(authServiceResponse.getCurrentUser()); + AuthServiceDto authServiceResponse = authService.connectToSalesforce(salesforceConnectDto, request); - return ResponseEntity.ok().headers(headers).body(salesforceConnectResponse); - } + HttpHeaders headers = new HttpHeaders(); + headers = cookieHelper.setUserCookie(authServiceResponse.getCurrentUserLoginCookie(), headers); - @PostMapping("/logout") - public ResponseEntity logout(HttpServletRequest request) { - logger.info("User logout request received"); + SalesforceConnectFormatterDto salesforceConnectResponse = new SalesforceConnectFormatterDto(); + salesforceConnectResponse.setCurrentUser(authServiceResponse.getCurrentUser()); - HttpHeaders headers = new HttpHeaders(); - headers = cookieHelper.clearUserCookie(headers); + return ResponseEntity.ok().headers(headers).body(salesforceConnectResponse); + } + + @PostMapping("/logout") + public ResponseEntity logout(HttpServletRequest request) { + logger.info("User logout request received"); + + HttpHeaders headers = new HttpHeaders(); + headers = cookieHelper.clearUserCookie(headers); + + return ResponseEntity.ok().headers(headers).body(null); + } + + @PostMapping("/disconnect") + public ResponseEntity disconnect(HttpServletRequest request) { + logger.info("User disconnect request received"); + + disconnectUserService.disconnect(request); + + logger.info("Clearing user cookie"); + HttpHeaders headers = new HttpHeaders(); + headers = cookieHelper.clearUserCookie(headers); + + logger.info("User disconnected successfully"); + return ResponseEntity.noContent().headers(headers).build(); + } - return ResponseEntity.ok().headers(headers).body(null); - } } diff --git a/src/main/java/com/salessparrow/api/controllers/CrmOrganizationUserController.java b/src/main/java/com/salessparrow/api/controllers/CrmOrganizationUserController.java new file mode 100644 index 00000000..53fce632 --- /dev/null +++ b/src/main/java/com/salessparrow/api/controllers/CrmOrganizationUserController.java @@ -0,0 +1,40 @@ +package com.salessparrow.api.controllers; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.salessparrow.api.dto.formatter.GetCrmOrganizationUsersFormatterDto; +import com.salessparrow.api.dto.requestMapper.GetCrmOrganizationUsersDto; +import com.salessparrow.api.services.crmOrganizationUsers.GetCrmOrganizationUsersList; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.Valid; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; + +@RestController +@RequestMapping("/api/v1/crm-organization-users") +public class CrmOrganizationUserController { + + Logger logger = LoggerFactory.getLogger(CrmOrganizationUserController.class); + + @Autowired + private GetCrmOrganizationUsersList getCrmOrganizationUsersList; + + @GetMapping("") + public ResponseEntity getCrmOrganizationUsers(HttpServletRequest request, + @Valid @ModelAttribute GetCrmOrganizationUsersDto CrmOrganizationUsersDto) { + logger.info("Get list of crm organization users request received"); + + GetCrmOrganizationUsersFormatterDto getCrmOrganizationUsersFormatterDto = getCrmOrganizationUsersList + .getCrmOrganizationUsers(request, CrmOrganizationUsersDto); + + return ResponseEntity.ok().body(getCrmOrganizationUsersFormatterDto); + } + +} diff --git a/src/main/java/com/salessparrow/api/controllers/HealthCheck.java b/src/main/java/com/salessparrow/api/controllers/HealthCheck.java new file mode 100644 index 00000000..e848c85b --- /dev/null +++ b/src/main/java/com/salessparrow/api/controllers/HealthCheck.java @@ -0,0 +1,17 @@ +package com.salessparrow.api.controllers; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/api/v1/health-check") +public class HealthCheck { + + @GetMapping("") + public ResponseEntity HealthCheck() { + return ResponseEntity.ok().body("OK"); + } + +} diff --git a/src/main/java/com/salessparrow/api/controllers/SuggestionsController.java b/src/main/java/com/salessparrow/api/controllers/SuggestionsController.java new file mode 100644 index 00000000..0bef0c1e --- /dev/null +++ b/src/main/java/com/salessparrow/api/controllers/SuggestionsController.java @@ -0,0 +1,35 @@ +package com.salessparrow.api.controllers; + +import org.slf4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.salessparrow.api.dto.formatter.CrmActionSuggestionsFormatterDto; +import com.salessparrow.api.dto.requestMapper.CrmActionsSuggestionsDto; +import com.salessparrow.api.services.suggestions.CrmActionsSuggestionsService; + +import jakarta.validation.Valid; + +@RestController +@RequestMapping("/api/v1/suggestions") +@Validated +public class SuggestionsController { + + private Logger logger = org.slf4j.LoggerFactory.getLogger(SuggestionsController.class); + + @Autowired + private CrmActionsSuggestionsService crmActionsSuggestionsService; + + @PostMapping("/crm-actions") + public CrmActionSuggestionsFormatterDto getCrmActionSuggestions( + @Valid @RequestBody CrmActionsSuggestionsDto crmActionsSuggestionsDto) { + logger.info("Crm actions suggestions request received"); + + return crmActionsSuggestionsService.getSuggestions(crmActionsSuggestionsDto); + } + +} diff --git a/src/main/java/com/salessparrow/api/controllers/UserController.java b/src/main/java/com/salessparrow/api/controllers/UserController.java index cad29cdc..7d4385f1 100644 --- a/src/main/java/com/salessparrow/api/controllers/UserController.java +++ b/src/main/java/com/salessparrow/api/controllers/UserController.java @@ -15,15 +15,16 @@ @RequestMapping("/api/v1/users") public class UserController { - @Autowired - private GetCurrentUserService getCurrentUserService; + @Autowired + private GetCurrentUserService getCurrentUserService; - @GetMapping("/current") - public ResponseEntity GetCurrentUser(HttpServletRequest request) { + @GetMapping("/current") + public ResponseEntity GetCurrentUser(HttpServletRequest request) { - GetCurrentUserFormatterDto getCurrentUserFormatterDto = new GetCurrentUserFormatterDto(); - getCurrentUserFormatterDto.setCurrentUser(getCurrentUserService.getCurrentUser(request)); + GetCurrentUserFormatterDto getCurrentUserFormatterDto = new GetCurrentUserFormatterDto(); + getCurrentUserFormatterDto.setCurrentUser(getCurrentUserService.getCurrentUser(request)); + + return ResponseEntity.ok().body(getCurrentUserFormatterDto); + } - return ResponseEntity.ok().body(getCurrentUserFormatterDto); - } } diff --git a/src/main/java/com/salessparrow/api/domain/SalesforceOauthToken.java b/src/main/java/com/salessparrow/api/domain/SalesforceOauthToken.java index b130f0b0..2916fcc6 100644 --- a/src/main/java/com/salessparrow/api/domain/SalesforceOauthToken.java +++ b/src/main/java/com/salessparrow/api/domain/SalesforceOauthToken.java @@ -18,90 +18,94 @@ /** * SalesforceOauthToken model. - * + * * Cached data is serialized into bytes and stored in cache and deserialize when - * retrieved. - * Hence, the class must implement Serializable. + * retrieved. Hence, the class must implement Serializable. */ @Data @NoArgsConstructor @DynamoDBTable(tableName = "salesforce_oauth_tokens") public class SalesforceOauthToken implements Serializable { - public enum Status { - ACTIVE(1), - DELETED(2); + public enum Status { - private final int value; - private static final Map map = new HashMap<>(); + ACTIVE(1), DELETED(2); - static { - for (Status status : Status.values()) { - map.put(status.value, status); - } - } + private final int value; - Status(int value) { - this.value = value; - } + private static final Map map = new HashMap<>(); - public int getValue() { - return value; - } + static { + for (Status status : Status.values()) { + map.put(status.value, status); + } + } - public static Status valueOf(int value) { - return map.get(value); - } - } + Status(int value) { + this.value = value; + } - @DynamoDBHashKey(attributeName = "external_user_id") - private String externalUserId; + public int getValue() { + return value; + } - @DynamoDBAttribute(attributeName = "identity_url") - private String identityUrl; + public static Status valueOf(int value) { + return map.get(value); + } - @DynamoDBAttribute(attributeName = "access_token") - private String accessToken; + } - @DynamoDBAttribute(attributeName = "refresh_token") - private String refreshToken; + @DynamoDBHashKey(attributeName = "external_user_id") + private String externalUserId; - @DynamoDBAttribute(attributeName = "signature") - private String signature; + @DynamoDBAttribute(attributeName = "identity_url") + private String identityUrl; - @DynamoDBAttribute(attributeName = "id_token") - private String idToken; + @DynamoDBAttribute(attributeName = "access_token") + private String accessToken; - @DynamoDBAttribute(attributeName = "instance_url") - private String instanceUrl; + @DynamoDBAttribute(attributeName = "refresh_token") + private String refreshToken; - @DynamoDBTypeConverted(converter = StatusEnumConverter.class) - @DynamoDBAttribute(attributeName = "status") - private Status status; + @DynamoDBAttribute(attributeName = "signature") + private String signature; - @DynamoDBAttribute(attributeName = "issued_at") - private Long issuedAt; + @DynamoDBAttribute(attributeName = "id_token") + private String idToken; - @DynamoDBAutoGeneratedTimestamp(strategy = DynamoDBAutoGenerateStrategy.CREATE) - @DynamoDBAttribute(attributeName = "created_at") - private Date createdAt; + @DynamoDBAttribute(attributeName = "instance_url") + private String instanceUrl; - @DynamoDBAutoGeneratedTimestamp(strategy = DynamoDBAutoGenerateStrategy.ALWAYS) - @DynamoDBAttribute(attributeName = "updated_at") - private Date updatedAt; + @DynamoDBTypeConverted(converter = StatusEnumConverter.class) + @DynamoDBAttribute(attributeName = "status") + private Status status; - /** - * Converts Status enum to Integer and vice versa. - */ - public static class StatusEnumConverter implements DynamoDBTypeConverter { - @Override - public Integer convert(Status status) { - return status.getValue(); - } + @DynamoDBAttribute(attributeName = "issued_at") + private Long issuedAt; + + @DynamoDBAutoGeneratedTimestamp(strategy = DynamoDBAutoGenerateStrategy.CREATE) + @DynamoDBAttribute(attributeName = "created_at") + private Date createdAt; + + @DynamoDBAutoGeneratedTimestamp(strategy = DynamoDBAutoGenerateStrategy.ALWAYS) + @DynamoDBAttribute(attributeName = "updated_at") + private Date updatedAt; + + /** + * Converts Status enum to Integer and vice versa. + */ + public static class StatusEnumConverter implements DynamoDBTypeConverter { + + @Override + public Integer convert(Status status) { + return status.getValue(); + } + + @Override + public Status unconvert(Integer value) { + return Status.valueOf(value); + } + + } - @Override - public Status unconvert(Integer value) { - return Status.valueOf(value); - } - } } diff --git a/src/main/java/com/salessparrow/api/domain/SalesforceOrganization.java b/src/main/java/com/salessparrow/api/domain/SalesforceOrganization.java index 97209234..97297a21 100644 --- a/src/main/java/com/salessparrow/api/domain/SalesforceOrganization.java +++ b/src/main/java/com/salessparrow/api/domain/SalesforceOrganization.java @@ -1,5 +1,6 @@ package com.salessparrow.api.domain; +import java.io.Serializable; import java.util.Date; import java.util.HashMap; import java.util.Map; @@ -18,61 +19,66 @@ @Data @NoArgsConstructor @DynamoDBTable(tableName = "salesforce_organizations") -public class SalesforceOrganization { - - public enum Status { - ACTIVE(1), - DELETED(2); - - private final int value; - private static final Map map = new HashMap<>(); - - static { - for (Status status : Status.values()) { - map.put(status.value, status); - } - } - - Status(int value) { - this.value = value; - } - - public int getValue() { - return value; - } - - public static Status valueOf(int value) { - return map.get(value); - } - } - - @DynamoDBHashKey(attributeName = "external_organization_id") - private String externalOrganizationId; - - @DynamoDBTypeConverted(converter = StatusEnumConverter.class) - @DynamoDBAttribute(attributeName = "status") - private Status status; - - @DynamoDBAutoGeneratedTimestamp(strategy = DynamoDBAutoGenerateStrategy.CREATE) - @DynamoDBAttribute(attributeName = "created_at") - private Date createdAt; - - @DynamoDBAutoGeneratedTimestamp(strategy = DynamoDBAutoGenerateStrategy.ALWAYS) - @DynamoDBAttribute(attributeName = "updated_at") - private Date updatedAt; - - /** - * Converts Status enum to Integer and vice versa. - */ - public static class StatusEnumConverter implements DynamoDBTypeConverter { - @Override - public Integer convert(Status status) { - return status.getValue(); - } - - @Override - public Status unconvert(Integer value) { - return Status.valueOf(value); - } - } +public class SalesforceOrganization implements Serializable { + + public enum Status { + + ACTIVE(1), DELETED(2); + + private final int value; + + private static final Map map = new HashMap<>(); + + static { + for (Status status : Status.values()) { + map.put(status.value, status); + } + } + + Status(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public static Status valueOf(int value) { + return map.get(value); + } + + } + + @DynamoDBHashKey(attributeName = "external_organization_id") + private String externalOrganizationId; + + @DynamoDBTypeConverted(converter = StatusEnumConverter.class) + @DynamoDBAttribute(attributeName = "status") + private Status status; + + @DynamoDBAutoGeneratedTimestamp(strategy = DynamoDBAutoGenerateStrategy.CREATE) + @DynamoDBAttribute(attributeName = "created_at") + private Date createdAt; + + @DynamoDBAutoGeneratedTimestamp(strategy = DynamoDBAutoGenerateStrategy.ALWAYS) + @DynamoDBAttribute(attributeName = "updated_at") + private Date updatedAt; + + /** + * Converts Status enum to Integer and vice versa. + */ + public static class StatusEnumConverter implements DynamoDBTypeConverter { + + @Override + public Integer convert(Status status) { + return status.getValue(); + } + + @Override + public Status unconvert(Integer value) { + return Status.valueOf(value); + } + + } + } diff --git a/src/main/java/com/salessparrow/api/domain/SalesforceUser.java b/src/main/java/com/salessparrow/api/domain/SalesforceUser.java index 89934262..6a5f84dd 100644 --- a/src/main/java/com/salessparrow/api/domain/SalesforceUser.java +++ b/src/main/java/com/salessparrow/api/domain/SalesforceUser.java @@ -19,94 +19,98 @@ /** * SalesforceUser model. - * + * * Cached data is serialized into bytes and stored in cache and deserialize when - * retrieved. - * Hence, the class must implement Serializable. + * retrieved. Hence, the class must implement Serializable. */ @Data @NoArgsConstructor @DynamoDBTable(tableName = "salesforce_users") public class SalesforceUser implements User, Serializable { - public enum Status { - ACTIVE(1), - DELETED(2); + public enum Status { - private final int value; - private static final Map map = new HashMap<>(); + ACTIVE(1), DELETED(2); - static { - for (Status status : Status.values()) { - map.put(status.value, status); - } - } + private final int value; - Status(int value) { - this.value = value; - } + private static final Map map = new HashMap<>(); - public int getValue() { - return value; - } + static { + for (Status status : Status.values()) { + map.put(status.value, status); + } + } - public static Status valueOf(int value) { - return map.get(value); - } - } + Status(int value) { + this.value = value; + } - @DynamoDBHashKey(attributeName = "external_user_id") - private String externalUserId; + public int getValue() { + return value; + } - @DynamoDBAttribute(attributeName = "identity_url") - private String identityUrl; + public static Status valueOf(int value) { + return map.get(value); + } - @DynamoDBAttribute(attributeName = "external_organization_id") - private String externalOrganizationId; + } - @DynamoDBAttribute(attributeName = "name") - private String name; + @DynamoDBHashKey(attributeName = "external_user_id") + private String externalUserId; - @DynamoDBAttribute(attributeName = "email") - private String email; + @DynamoDBAttribute(attributeName = "identity_url") + private String identityUrl; - @DynamoDBAttribute(attributeName = "user_kind") - private String userKind; + @DynamoDBAttribute(attributeName = "external_organization_id") + private String externalOrganizationId; - @DynamoDBAttribute(attributeName = "cookie_token") - private String cookieToken; + @DynamoDBAttribute(attributeName = "name") + private String name; - @DynamoDBAttribute(attributeName = "encryption_salt") - private String encryptionSalt; + @DynamoDBAttribute(attributeName = "email") + private String email; - @DynamoDBTypeConverted(converter = StatusEnumConverter.class) - @DynamoDBAttribute(attributeName = "status") - private Status status; + @DynamoDBAttribute(attributeName = "user_kind") + private String userKind; - @DynamoDBAutoGeneratedTimestamp(strategy = DynamoDBAutoGenerateStrategy.CREATE) - @DynamoDBAttribute(attributeName = "created_at") - private Date createdAt; + @DynamoDBAttribute(attributeName = "cookie_token") + private String cookieToken; - @DynamoDBAutoGeneratedTimestamp(strategy = DynamoDBAutoGenerateStrategy.ALWAYS) - @DynamoDBAttribute(attributeName = "updated_at") - private Date updatedAt; + @DynamoDBAttribute(attributeName = "encryption_salt") + private String encryptionSalt; - /** - * Converts Status enum to Integer and vice versa. - */ - public static class StatusEnumConverter implements DynamoDBTypeConverter { - @Override - public Integer convert(Status status) { - return status.getValue(); - } + @DynamoDBTypeConverted(converter = StatusEnumConverter.class) + @DynamoDBAttribute(attributeName = "status") + private Status status; - @Override - public Status unconvert(Integer value) { - return Status.valueOf(value); - } - } + @DynamoDBAutoGeneratedTimestamp(strategy = DynamoDBAutoGenerateStrategy.CREATE) + @DynamoDBAttribute(attributeName = "created_at") + private Date createdAt; + + @DynamoDBAutoGeneratedTimestamp(strategy = DynamoDBAutoGenerateStrategy.ALWAYS) + @DynamoDBAttribute(attributeName = "updated_at") + private Date updatedAt; + + /** + * Converts Status enum to Integer and vice versa. + */ + public static class StatusEnumConverter implements DynamoDBTypeConverter { + + @Override + public Integer convert(Status status) { + return status.getValue(); + } + + @Override + public Status unconvert(Integer value) { + return Status.valueOf(value); + } + + } + + public String getId(String externalUserId) { + return UserConstants.SALESFORCE_USER_KIND + "-" + externalUserId; + } - public String getId(String externalUserId) { - return UserConstants.SALESFORCE_USER_KIND + "-" + externalUserId; - } } \ No newline at end of file diff --git a/src/main/java/com/salessparrow/api/domain/User.java b/src/main/java/com/salessparrow/api/domain/User.java index cfcfa90c..a141ea4d 100644 --- a/src/main/java/com/salessparrow/api/domain/User.java +++ b/src/main/java/com/salessparrow/api/domain/User.java @@ -5,16 +5,16 @@ */ public interface User { - String getExternalUserId(); + String getExternalUserId(); - String getEmail(); + String getEmail(); - String getName(); + String getName(); - String getUserKind(); + String getUserKind(); - String getCookieToken(); + String getCookieToken(); - String getEncryptionSalt(); + String getEncryptionSalt(); } diff --git a/src/main/java/com/salessparrow/api/dto/entities/AccountContactAssociationsEntity.java b/src/main/java/com/salessparrow/api/dto/entities/AccountContactAssociationsEntity.java new file mode 100644 index 00000000..5c4219b1 --- /dev/null +++ b/src/main/java/com/salessparrow/api/dto/entities/AccountContactAssociationsEntity.java @@ -0,0 +1,16 @@ +package com.salessparrow.api.dto.entities; + +import java.util.List; + +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; + +import lombok.Data; + +@Data +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +public class AccountContactAssociationsEntity { + + private List contactIds; + +} diff --git a/src/main/java/com/salessparrow/api/dto/entities/AccountEntity.java b/src/main/java/com/salessparrow/api/dto/entities/AccountEntity.java index 49b615f9..ae03764d 100644 --- a/src/main/java/com/salessparrow/api/dto/entities/AccountEntity.java +++ b/src/main/java/com/salessparrow/api/dto/entities/AccountEntity.java @@ -1,9 +1,22 @@ package com.salessparrow.api.dto.entities; +import java.util.Map; + +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; + import lombok.Data; @Data +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) public class AccountEntity { - private String id; - private String name; + + private String id; + + private String name; + + private Map additionalFields; + + private String accountContactAssociationsId; + } \ No newline at end of file diff --git a/src/main/java/com/salessparrow/api/dto/entities/AddTaskSuggestionEntityDto.java b/src/main/java/com/salessparrow/api/dto/entities/AddTaskSuggestionEntityDto.java new file mode 100644 index 00000000..6f631232 --- /dev/null +++ b/src/main/java/com/salessparrow/api/dto/entities/AddTaskSuggestionEntityDto.java @@ -0,0 +1,19 @@ +package com.salessparrow.api.dto.entities; + +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; + +import lombok.Data; + +/** + * Add Task Suggestion Entity is a DTO class for the Add Task Suggestion Entity. + */ +@Data +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +public class AddTaskSuggestionEntityDto { + + private String description; + + private String dueDate; + +} diff --git a/src/main/java/com/salessparrow/api/dto/entities/ContactEntity.java b/src/main/java/com/salessparrow/api/dto/entities/ContactEntity.java new file mode 100644 index 00000000..cbd0e200 --- /dev/null +++ b/src/main/java/com/salessparrow/api/dto/entities/ContactEntity.java @@ -0,0 +1,20 @@ +package com.salessparrow.api.dto.entities; + +import java.util.Map; + +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; + +import lombok.Data; + +@Data +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +public class ContactEntity { + + private String id; + + private String name; + + private Map additionalFields; + +} diff --git a/src/main/java/com/salessparrow/api/dto/entities/CrmOrganizationUserEntity.java b/src/main/java/com/salessparrow/api/dto/entities/CrmOrganizationUserEntity.java new file mode 100644 index 00000000..bb2d65b9 --- /dev/null +++ b/src/main/java/com/salessparrow/api/dto/entities/CrmOrganizationUserEntity.java @@ -0,0 +1,12 @@ +package com.salessparrow.api.dto.entities; + +import lombok.Data; + +@Data +public class CrmOrganizationUserEntity { + + private String id; + + private String name; + +} diff --git a/src/main/java/com/salessparrow/api/dto/entities/CurrentUserEntityDto.java b/src/main/java/com/salessparrow/api/dto/entities/CurrentUserEntityDto.java index a7a5aedb..77412bd5 100644 --- a/src/main/java/com/salessparrow/api/dto/entities/CurrentUserEntityDto.java +++ b/src/main/java/com/salessparrow/api/dto/entities/CurrentUserEntityDto.java @@ -2,39 +2,40 @@ /** * Salesforce connect formatter DTO. - * + * * @param current_user - * * @return SalesforceConnectFormatterDto */ public class CurrentUserEntityDto { - private String id; - private String name; - private String email; + private String id; - public String getId() { - return id; - } + private String name; - public void setId(String id) { - this.id = id; - } + private String email; - public String getName() { - return name; - } + public String getId() { + return id; + } - public void setName(String name) { - this.name = name; - } + public void setId(String id) { + this.id = id; + } - public String getEmail() { - return email; - } + public String getName() { + return name; + } - public void setEmail(String email) { - this.email = email; - } + public void setName(String name) { + this.name = name; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } } diff --git a/src/main/java/com/salessparrow/api/dto/entities/NextPagePayloadEntity.java b/src/main/java/com/salessparrow/api/dto/entities/NextPagePayloadEntity.java new file mode 100644 index 00000000..12636819 --- /dev/null +++ b/src/main/java/com/salessparrow/api/dto/entities/NextPagePayloadEntity.java @@ -0,0 +1,14 @@ +package com.salessparrow.api.dto.entities; + +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; + +import lombok.Data; + +@Data +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +public class NextPagePayloadEntity { + + private String paginationIdentifier; + +} diff --git a/src/main/java/com/salessparrow/api/dto/entities/NoteDetailEntity.java b/src/main/java/com/salessparrow/api/dto/entities/NoteDetailEntity.java index 34794e18..bc327da1 100644 --- a/src/main/java/com/salessparrow/api/dto/entities/NoteDetailEntity.java +++ b/src/main/java/com/salessparrow/api/dto/entities/NoteDetailEntity.java @@ -13,18 +13,23 @@ @Data @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) public class NoteDetailEntity { - private String id; - private String creator; - private String text; - private Date lastModifiedTime; - - public NoteDetailEntity(){ - } - - public NoteDetailEntity(String id, String creator, String text, Date lastModifiedTime){ - this.id = id; - this.creator = creator; - this.text = text; - this.lastModifiedTime = lastModifiedTime; - } + + private String id; + + private String creator; + + private String text; + + private Date lastModifiedTime; + + public NoteDetailEntity() { + } + + public NoteDetailEntity(String id, String creator, String text, Date lastModifiedTime) { + this.id = id; + this.creator = creator; + this.text = text; + this.lastModifiedTime = lastModifiedTime; + } + } diff --git a/src/main/java/com/salessparrow/api/dto/entities/NoteEntity.java b/src/main/java/com/salessparrow/api/dto/entities/NoteEntity.java index 77897761..747cd761 100644 --- a/src/main/java/com/salessparrow/api/dto/entities/NoteEntity.java +++ b/src/main/java/com/salessparrow/api/dto/entities/NoteEntity.java @@ -13,19 +13,23 @@ @Data @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) public class NoteEntity { - private String id; - private String creator; - private String textPreview; - private Date lastModifiedTime; - - public NoteEntity() { - } - - public NoteEntity(String id, String creator, String text_preview, Date last_modified_time) { - this.id = id; - this.creator = creator; - this.textPreview = text_preview; - this.lastModifiedTime = last_modified_time; - } + + private String id; + + private String creator; + + private String textPreview; + + private Date lastModifiedTime; + + public NoteEntity() { + } + + public NoteEntity(String id, String creator, String text_preview, Date last_modified_time) { + this.id = id; + this.creator = creator; + this.textPreview = text_preview; + this.lastModifiedTime = last_modified_time; + } } diff --git a/src/main/java/com/salessparrow/api/dto/entities/TaskEntity.java b/src/main/java/com/salessparrow/api/dto/entities/TaskEntity.java new file mode 100644 index 00000000..ba1a199e --- /dev/null +++ b/src/main/java/com/salessparrow/api/dto/entities/TaskEntity.java @@ -0,0 +1,29 @@ +package com.salessparrow.api.dto.entities; + +import java.util.Date; + +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; + +import lombok.Data; + +/** + * TaskEntity is a DTO class for the Task List. + */ +@Data +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +public class TaskEntity { + + private String id; + + private String creatorName; + + private String description; + + private String dueDate; + + private String crmOrganizationUserName; + + private Date lastModifiedTime; + +} diff --git a/src/main/java/com/salessparrow/api/dto/formatter/CreateNoteFormatterDto.java b/src/main/java/com/salessparrow/api/dto/formatter/CreateNoteFormatterDto.java index 1cfc1cd4..7faa1ebd 100644 --- a/src/main/java/com/salessparrow/api/dto/formatter/CreateNoteFormatterDto.java +++ b/src/main/java/com/salessparrow/api/dto/formatter/CreateNoteFormatterDto.java @@ -11,5 +11,7 @@ @Data @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) public class CreateNoteFormatterDto { - private String noteId; + + private String noteId; + } \ No newline at end of file diff --git a/src/main/java/com/salessparrow/api/dto/formatter/CreateTaskFormatterDto.java b/src/main/java/com/salessparrow/api/dto/formatter/CreateTaskFormatterDto.java new file mode 100644 index 00000000..2dabdab6 --- /dev/null +++ b/src/main/java/com/salessparrow/api/dto/formatter/CreateTaskFormatterDto.java @@ -0,0 +1,14 @@ +package com.salessparrow.api.dto.formatter; + +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; + +import lombok.Data; + +@Data +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +public class CreateTaskFormatterDto { + + private String taskId; + +} diff --git a/src/main/java/com/salessparrow/api/dto/formatter/CrmActionSuggestionsFormatterDto.java b/src/main/java/com/salessparrow/api/dto/formatter/CrmActionSuggestionsFormatterDto.java new file mode 100644 index 00000000..36d39ad7 --- /dev/null +++ b/src/main/java/com/salessparrow/api/dto/formatter/CrmActionSuggestionsFormatterDto.java @@ -0,0 +1,21 @@ +package com.salessparrow.api.dto.formatter; + +import java.util.List; + +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import com.salessparrow.api.dto.entities.AddTaskSuggestionEntityDto; + +import lombok.Data; + +/** + * CrmActionSuggestionsFormatterDto is a class for the formatter of the crm action + * suggestions. + */ +@Data +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +public class CrmActionSuggestionsFormatterDto { + + private List addTaskSuggestions; + +} diff --git a/src/main/java/com/salessparrow/api/dto/formatter/GetAccountsFormatterDto.java b/src/main/java/com/salessparrow/api/dto/formatter/GetAccountsFormatterDto.java index 7a0db924..9ccecd4c 100644 --- a/src/main/java/com/salessparrow/api/dto/formatter/GetAccountsFormatterDto.java +++ b/src/main/java/com/salessparrow/api/dto/formatter/GetAccountsFormatterDto.java @@ -7,11 +7,20 @@ import com.fasterxml.jackson.databind.PropertyNamingStrategies; import com.fasterxml.jackson.databind.annotation.JsonNaming; +import com.salessparrow.api.dto.entities.AccountContactAssociationsEntity; import com.salessparrow.api.dto.entities.AccountEntity; +import com.salessparrow.api.dto.entities.ContactEntity; @Data @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) public class GetAccountsFormatterDto { - private List accountIds; - private Map accountMapById; + + private List accountIds; + + private Map accountMapById; + + private Map contactMapById; + + private Map accountContactAssociationsMapById; + } \ No newline at end of file diff --git a/src/main/java/com/salessparrow/api/dto/formatter/GetCrmOrganizationUsersFormatterDto.java b/src/main/java/com/salessparrow/api/dto/formatter/GetCrmOrganizationUsersFormatterDto.java new file mode 100644 index 00000000..2f5f9841 --- /dev/null +++ b/src/main/java/com/salessparrow/api/dto/formatter/GetCrmOrganizationUsersFormatterDto.java @@ -0,0 +1,20 @@ +package com.salessparrow.api.dto.formatter; + +import java.util.List; +import java.util.Map; + +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import com.salessparrow.api.dto.entities.CrmOrganizationUserEntity; + +import lombok.Data; + +@Data +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +public class GetCrmOrganizationUsersFormatterDto { + + private List crmOrganizationUserIds; + + private Map crmOrganizationUserMapById; + +} diff --git a/src/main/java/com/salessparrow/api/dto/formatter/GetCurrentUserFormatterDto.java b/src/main/java/com/salessparrow/api/dto/formatter/GetCurrentUserFormatterDto.java index bf422cfe..b135be02 100644 --- a/src/main/java/com/salessparrow/api/dto/formatter/GetCurrentUserFormatterDto.java +++ b/src/main/java/com/salessparrow/api/dto/formatter/GetCurrentUserFormatterDto.java @@ -8,13 +8,14 @@ /** * Get current user formatter DTO. - * + * * @param current_user - * * @return GetCurrentUserFormatterDto */ @Data @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) public class GetCurrentUserFormatterDto { - private CurrentUserEntityDto currentUser; + + private CurrentUserEntityDto currentUser; + } \ No newline at end of file diff --git a/src/main/java/com/salessparrow/api/dto/formatter/GetNoteDetailsFormatterDto.java b/src/main/java/com/salessparrow/api/dto/formatter/GetNoteDetailsFormatterDto.java index 92425e03..3424b6ae 100644 --- a/src/main/java/com/salessparrow/api/dto/formatter/GetNoteDetailsFormatterDto.java +++ b/src/main/java/com/salessparrow/api/dto/formatter/GetNoteDetailsFormatterDto.java @@ -10,9 +10,10 @@ @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) public class GetNoteDetailsFormatterDto { - private NoteDetailEntity noteDetail; - - public GetNoteDetailsFormatterDto(NoteDetailEntity noteDetail){ - this.noteDetail = noteDetail; - } + private NoteDetailEntity noteDetail; + + public GetNoteDetailsFormatterDto(NoteDetailEntity noteDetail) { + this.noteDetail = noteDetail; + } + } diff --git a/src/main/java/com/salessparrow/api/dto/formatter/GetNotesListFormatterDto.java b/src/main/java/com/salessparrow/api/dto/formatter/GetNotesListFormatterDto.java index e9390d75..884f1cd0 100644 --- a/src/main/java/com/salessparrow/api/dto/formatter/GetNotesListFormatterDto.java +++ b/src/main/java/com/salessparrow/api/dto/formatter/GetNotesListFormatterDto.java @@ -15,14 +15,17 @@ @Data @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) public class GetNotesListFormatterDto { - private List noteIds; - private Map noteMapById; - public GetNotesListFormatterDto(){ - } + private List noteIds; + + private Map noteMapById; + + public GetNotesListFormatterDto() { + } + + public GetNotesListFormatterDto(List noteIds, Map noteMapById) { + this.noteIds = noteIds; + this.noteMapById = noteMapById; + } - public GetNotesListFormatterDto(List noteIds, Map noteMapById){ - this.noteIds = noteIds; - this.noteMapById = noteMapById; - } } diff --git a/src/main/java/com/salessparrow/api/dto/formatter/GetTasksListFormatterDto.java b/src/main/java/com/salessparrow/api/dto/formatter/GetTasksListFormatterDto.java new file mode 100644 index 00000000..51e100e5 --- /dev/null +++ b/src/main/java/com/salessparrow/api/dto/formatter/GetTasksListFormatterDto.java @@ -0,0 +1,23 @@ +package com.salessparrow.api.dto.formatter; + +import java.util.List; +import java.util.Map; + +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import com.salessparrow.api.dto.entities.TaskEntity; + +import lombok.Data; + +/** + * GetTasksListFormatterDto is a DTO class for the GetTasksListFormatterDto response. + */ +@Data +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +public class GetTasksListFormatterDto { + + private List taskIds; + + private Map taskMapById; + +} diff --git a/src/main/java/com/salessparrow/api/dto/formatter/PaginationIdentifierFormatterDto.java b/src/main/java/com/salessparrow/api/dto/formatter/PaginationIdentifierFormatterDto.java new file mode 100644 index 00000000..06728a94 --- /dev/null +++ b/src/main/java/com/salessparrow/api/dto/formatter/PaginationIdentifierFormatterDto.java @@ -0,0 +1,10 @@ +package com.salessparrow.api.dto.formatter; + +import lombok.Data; + +@Data +public class PaginationIdentifierFormatterDto { + + private Integer pageNumber; + +} diff --git a/src/main/java/com/salessparrow/api/dto/formatter/RedirectUrlFormatterDto.java b/src/main/java/com/salessparrow/api/dto/formatter/RedirectUrlFormatterDto.java index 42e7c4e7..f2b1db26 100644 --- a/src/main/java/com/salessparrow/api/dto/formatter/RedirectUrlFormatterDto.java +++ b/src/main/java/com/salessparrow/api/dto/formatter/RedirectUrlFormatterDto.java @@ -7,13 +7,14 @@ /** * Redirect url formatter DTO. - * + * * @param url - * * @return RedirectUrlFormatterDto */ @Data @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) public class RedirectUrlFormatterDto { - private String url; + + private String url; + } diff --git a/src/main/java/com/salessparrow/api/dto/formatter/SalesforceConnectFormatterDto.java b/src/main/java/com/salessparrow/api/dto/formatter/SalesforceConnectFormatterDto.java index bfdfb918..e25bf0a9 100644 --- a/src/main/java/com/salessparrow/api/dto/formatter/SalesforceConnectFormatterDto.java +++ b/src/main/java/com/salessparrow/api/dto/formatter/SalesforceConnectFormatterDto.java @@ -8,17 +8,17 @@ /** * Salesforce connect formatter DTO. - * + * * @param current_user - * * @return SalesforceConnectFormatterDto */ @Data @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) public class SalesforceConnectFormatterDto { - public SalesforceConnectFormatterDto() { - } - private CurrentUserEntityDto currentUser; + public SalesforceConnectFormatterDto() { + } + + private CurrentUserEntityDto currentUser; } diff --git a/src/main/java/com/salessparrow/api/dto/requestMapper/CreateAccountTaskDto.java b/src/main/java/com/salessparrow/api/dto/requestMapper/CreateAccountTaskDto.java new file mode 100644 index 00000000..dc432867 --- /dev/null +++ b/src/main/java/com/salessparrow/api/dto/requestMapper/CreateAccountTaskDto.java @@ -0,0 +1,25 @@ +package com.salessparrow.api.dto.requestMapper; + +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import com.salessparrow.api.lib.customAnnotations.ValidDateFormat; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; +import lombok.Data; + +@Data +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +public class CreateAccountTaskDto { + + @NotBlank(message = "missing_crm_organization_user_id") + private String crmOrganizationUserId; + + @NotBlank(message = "missing_description") + @Size(max = 32000, message = "description_too_long") + private String description; + + @ValidDateFormat(message = "invalid_due_date") + private String dueDate; + +} diff --git a/src/main/java/com/salessparrow/api/dto/requestMapper/CrmActionsSuggestionsDto.java b/src/main/java/com/salessparrow/api/dto/requestMapper/CrmActionsSuggestionsDto.java new file mode 100644 index 00000000..a38fab64 --- /dev/null +++ b/src/main/java/com/salessparrow/api/dto/requestMapper/CrmActionsSuggestionsDto.java @@ -0,0 +1,18 @@ +package com.salessparrow.api.dto.requestMapper; + +import org.hibernate.validator.constraints.Length; + +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +/** + * CrmActionsSuggestionsDto is a dto class for the crm actions suggestions. + */ +@Data +public class CrmActionsSuggestionsDto { + + @NotBlank(message = "missing_text") + @Length(max = 12000, message = "text_too_long") + private String text; + +} diff --git a/src/main/java/com/salessparrow/api/dto/requestMapper/GetAccountsDto.java b/src/main/java/com/salessparrow/api/dto/requestMapper/GetAccountsDto.java index 573faf67..a9c434d3 100644 --- a/src/main/java/com/salessparrow/api/dto/requestMapper/GetAccountsDto.java +++ b/src/main/java/com/salessparrow/api/dto/requestMapper/GetAccountsDto.java @@ -5,6 +5,8 @@ @Data public class GetAccountsDto { - @Size(max = 200, message = "search_term_too_long") - private String q; + + @Size(max = 200, message = "search_term_too_long") + private String q; + } diff --git a/src/main/java/com/salessparrow/api/dto/requestMapper/GetAccountsFeedDto.java b/src/main/java/com/salessparrow/api/dto/requestMapper/GetAccountsFeedDto.java new file mode 100644 index 00000000..bec24967 --- /dev/null +++ b/src/main/java/com/salessparrow/api/dto/requestMapper/GetAccountsFeedDto.java @@ -0,0 +1,16 @@ +package com.salessparrow.api.dto.requestMapper; + +import com.fasterxml.jackson.databind.PropertyNamingStrategies.LowerCamelCaseStrategy; +import com.fasterxml.jackson.databind.annotation.JsonNaming; + +import jakarta.validation.constraints.Pattern; +import lombok.Data; + +@Data +@JsonNaming(LowerCamelCaseStrategy.class) +public class GetAccountsFeedDto { + + @Pattern(regexp = "^[A-Za-z0-9+/]*={0,2}$", message = "invalid_pagination_identifier") + private String pagination_identifier; + +} diff --git a/src/main/java/com/salessparrow/api/dto/requestMapper/GetCrmOrganizationUsersDto.java b/src/main/java/com/salessparrow/api/dto/requestMapper/GetCrmOrganizationUsersDto.java new file mode 100644 index 00000000..7d3ee84e --- /dev/null +++ b/src/main/java/com/salessparrow/api/dto/requestMapper/GetCrmOrganizationUsersDto.java @@ -0,0 +1,12 @@ +package com.salessparrow.api.dto.requestMapper; + +import jakarta.validation.constraints.Size; +import lombok.Data; + +@Data +public class GetCrmOrganizationUsersDto { + + @Size(max = 200, message = "search_term_too_long") + private String q; + +} diff --git a/src/main/java/com/salessparrow/api/dto/requestMapper/NoteDto.java b/src/main/java/com/salessparrow/api/dto/requestMapper/NoteDto.java index e2a3646c..44ca28f6 100644 --- a/src/main/java/com/salessparrow/api/dto/requestMapper/NoteDto.java +++ b/src/main/java/com/salessparrow/api/dto/requestMapper/NoteDto.java @@ -1,13 +1,15 @@ package com.salessparrow.api.dto.requestMapper; +import org.hibernate.validator.constraints.Length; + import jakarta.validation.constraints.NotBlank; import lombok.Data; @Data public class NoteDto { - @NotBlank(message = "missing_text") - private String text; -} - + @NotBlank(message = "missing_text") + @Length(max = 12000, message = "text_too_long") + private String text; +} diff --git a/src/main/java/com/salessparrow/api/dto/requestMapper/SalesforceConnectDto.java b/src/main/java/com/salessparrow/api/dto/requestMapper/SalesforceConnectDto.java index 5b5dec38..efc21d0f 100644 --- a/src/main/java/com/salessparrow/api/dto/requestMapper/SalesforceConnectDto.java +++ b/src/main/java/com/salessparrow/api/dto/requestMapper/SalesforceConnectDto.java @@ -6,40 +6,39 @@ /** * Salesforce connect DTO. - * + * * @param code * @param redirect_uri - * * @return SalesforceConnectDto */ public class SalesforceConnectDto { - @NotBlank(message = "missing_code") - private String code; + @NotBlank(message = "missing_code") + private String code; - @NotBlank(message = "missing_redirect_uri") - @ValidRedirectUri(message = "invalid_redirect_uri") - private String redirect_uri; + @NotBlank(message = "missing_redirect_uri") + @ValidRedirectUri(message = "invalid_redirect_uri") + private String redirect_uri; - public String getCode() { - return code; - } + public String getCode() { + return code; + } - public void setCode(String code) { - this.code = code; - } + public void setCode(String code) { + this.code = code; + } - public String getRedirect_uri() { - return redirect_uri; - } + public String getRedirect_uri() { + return redirect_uri; + } - public void setRedirect_uri(String redirect_uri) { - this.redirect_uri = redirect_uri; - } + public void setRedirect_uri(String redirect_uri) { + this.redirect_uri = redirect_uri; + } - @Override - public String toString() { - return "SalesforceConnectDto [code=" + code + ", redirect_uri=" + redirect_uri + "]"; - } + @Override + public String toString() { + return "SalesforceConnectDto [code=" + code + ", redirect_uri=" + redirect_uri + "]"; + } } diff --git a/src/main/java/com/salessparrow/api/dto/requestMapper/SalesforceRedirectUrlDto.java b/src/main/java/com/salessparrow/api/dto/requestMapper/SalesforceRedirectUrlDto.java index 8ddf1824..670e6890 100644 --- a/src/main/java/com/salessparrow/api/dto/requestMapper/SalesforceRedirectUrlDto.java +++ b/src/main/java/com/salessparrow/api/dto/requestMapper/SalesforceRedirectUrlDto.java @@ -7,18 +7,18 @@ /** * Redirect url DTO. - * + * * @param redirect_uri * @param state - * * @return SalesforceRedirectUrlDto */ @Data public class SalesforceRedirectUrlDto { - @NotBlank(message = "missing_redirect_uri") - @ValidRedirectUri(message = "invalid_redirect_uri") - private String redirect_uri; + @NotBlank(message = "missing_redirect_uri") + @ValidRedirectUri(message = "invalid_redirect_uri") + private String redirect_uri; + + private String state; - private String state; } diff --git a/src/main/java/com/salessparrow/api/dto/responseMapper/GetAccountListResponseDto.java b/src/main/java/com/salessparrow/api/dto/responseMapper/GetAccountListResponseDto.java new file mode 100644 index 00000000..3c4b4c16 --- /dev/null +++ b/src/main/java/com/salessparrow/api/dto/responseMapper/GetAccountListResponseDto.java @@ -0,0 +1,20 @@ +package com.salessparrow.api.dto.responseMapper; + +import java.util.List; +import java.util.Map; + +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import com.salessparrow.api.dto.entities.AccountEntity; + +import lombok.Data; + +@Data +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +public class GetAccountListResponseDto { + + private List accountIds; + + private Map accountMapById; + +} diff --git a/src/main/java/com/salessparrow/api/dto/responseMapper/GetAccountsFeedResponseDto.java b/src/main/java/com/salessparrow/api/dto/responseMapper/GetAccountsFeedResponseDto.java new file mode 100644 index 00000000..b3868931 --- /dev/null +++ b/src/main/java/com/salessparrow/api/dto/responseMapper/GetAccountsFeedResponseDto.java @@ -0,0 +1,29 @@ +package com.salessparrow.api.dto.responseMapper; + +import java.util.List; +import java.util.Map; + +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import com.salessparrow.api.dto.entities.AccountContactAssociationsEntity; +import com.salessparrow.api.dto.entities.AccountEntity; +import com.salessparrow.api.dto.entities.ContactEntity; +import com.salessparrow.api.dto.entities.NextPagePayloadEntity; + +import lombok.Data; + +@Data +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +public class GetAccountsFeedResponseDto { + + private List accountIds; + + private Map accountMapById; + + private Map contactMapById; + + private Map accountContactAssociationsMapById; + + private NextPagePayloadEntity nextPagePayload; + +} diff --git a/src/main/java/com/salessparrow/api/exception/CustomException.java b/src/main/java/com/salessparrow/api/exception/CustomException.java index 928c5bdb..4ddcd91b 100644 --- a/src/main/java/com/salessparrow/api/exception/CustomException.java +++ b/src/main/java/com/salessparrow/api/exception/CustomException.java @@ -4,22 +4,25 @@ import com.salessparrow.api.lib.errorLib.ParamErrorObject; public class CustomException extends RuntimeException { - private ErrorObject errorObject; - private ParamErrorObject paramErrorObject; - public CustomException(ErrorObject errorObject) { - this.errorObject = errorObject; - } + private ErrorObject errorObject; - public CustomException(ParamErrorObject paramErrorObject) { - this.paramErrorObject = paramErrorObject; - } + private ParamErrorObject paramErrorObject; - public ParamErrorObject getParamErrorObject() { - return paramErrorObject; - } + public CustomException(ErrorObject errorObject) { + this.errorObject = errorObject; + } + + public CustomException(ParamErrorObject paramErrorObject) { + this.paramErrorObject = paramErrorObject; + } + + public ParamErrorObject getParamErrorObject() { + return paramErrorObject; + } + + public ErrorObject getErrorObject() { + return errorObject; + } - public ErrorObject getErrorObject() { - return errorObject; - } } \ No newline at end of file diff --git a/src/main/java/com/salessparrow/api/exception/ErrorResponse.java b/src/main/java/com/salessparrow/api/exception/ErrorResponse.java index f69d0ba8..b31be450 100644 --- a/src/main/java/com/salessparrow/api/exception/ErrorResponse.java +++ b/src/main/java/com/salessparrow/api/exception/ErrorResponse.java @@ -21,105 +21,97 @@ @Component public class ErrorResponse { - @Autowired - private ResourceLoader resourceLoader; - - /** - * Get error response - * - * @param apiIdentifier - * @param internalErrorIdentifier - * - * @return ErrorResponseObject - */ - protected ErrorResponseObject getErrorResponse(String apiIdentifier, String internalErrorIdentifier, String message) { - - String errorConfigPath = "classpath:config/ApiErrorConfig.json"; - Resource resource = resourceLoader.getResource(errorConfigPath); - ObjectMapper objectMapper = new ObjectMapper(); - Map errorDataMap = new HashMap<>(); - try { - errorDataMap = objectMapper.readValue(resource.getInputStream(), - new TypeReference>() { - }); - } catch (Exception e) { - e.printStackTrace(); - throw new RuntimeException("Error while reading error config file:" + e.getMessage()); - } - - ErrorConfig errorInfo = errorDataMap.get(apiIdentifier); - - if (errorInfo == null) { - errorInfo = errorDataMap.get("something_went_wrong"); - } - - - ErrorResponseObject errorResponseObject = new ErrorResponseObject( - Integer.parseInt(errorInfo.getHttpCode()), - errorInfo.getMessage(), - errorInfo.getCode(), - internalErrorIdentifier, - new ArrayList()); - - return errorResponseObject; - } - - /** - * Get error response - * - * @param internalErrorIdentifier - * @param message - * @param paramErrorIdentifiers - * - * @return ErrorResponseObject - */ - protected ErrorResponseObject getParamErrorResponse(String internalErrorIdentifier, String message, - List paramErrorIdentifiers) { - - String paramsErrorPath = "classpath:config/ParamErrorConfig.json"; - Resource resource = resourceLoader.getResource(paramsErrorPath); - ObjectMapper objectMapper = new ObjectMapper(); - Map paramErrorDataMap = new HashMap<>(); - try { - paramErrorDataMap = objectMapper.readValue(resource.getInputStream(), - new TypeReference>() { - }); - } catch (Exception e) { - e.printStackTrace(); - throw new RuntimeException("Error while reading param error config file:" + e.getMessage()); - } - - List paramErrorConfigList = new ArrayList(); - - for (String paramErrorIdentifier : paramErrorIdentifiers) { - ParamErrorConfig paramErrorConfig = null; - - Pattern pattern = Pattern.compile("^missing_(.*)$"); - Matcher matcher = pattern.matcher(paramErrorIdentifier); - - if (matcher.matches()) { - String paramName = matcher.group(1); - String messageString = paramName + " is required parameter. Please provide " + paramName + "."; - - paramErrorConfig = new ParamErrorConfig(paramName, paramErrorIdentifier, messageString); - paramErrorConfigList.add(paramErrorConfig); - } - else { - paramErrorConfig = paramErrorDataMap.get(paramErrorIdentifier); - if (paramErrorConfig != null) { - paramErrorConfigList.add(paramErrorConfig); - } - } - } - - ErrorResponseObject errorResponseObject = new ErrorResponseObject( - 400, - "At least one parameter is invalid or missing.", - "INVALID_PARAMS", - internalErrorIdentifier, - paramErrorConfigList); - - return errorResponseObject; - } + @Autowired + private ResourceLoader resourceLoader; + + /** + * Get error response + * @param apiIdentifier + * @param internalErrorIdentifier + * @return ErrorResponseObject + */ + protected ErrorResponseObject getErrorResponse(String apiIdentifier, String internalErrorIdentifier, + String message) { + + String errorConfigPath = "classpath:config/ApiErrorConfig.json"; + Resource resource = resourceLoader.getResource(errorConfigPath); + ObjectMapper objectMapper = new ObjectMapper(); + Map errorDataMap = new HashMap<>(); + try { + errorDataMap = objectMapper.readValue(resource.getInputStream(), + new TypeReference>() { + }); + } + catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException("Error while reading error config file:" + e.getMessage()); + } + + ErrorConfig errorInfo = errorDataMap.get(apiIdentifier); + + if (errorInfo == null) { + errorInfo = errorDataMap.get("something_went_wrong"); + } + + ErrorResponseObject errorResponseObject = new ErrorResponseObject(Integer.parseInt(errorInfo.getHttpCode()), + errorInfo.getMessage(), errorInfo.getCode(), internalErrorIdentifier, + new ArrayList()); + + return errorResponseObject; + } + + /** + * Get error response + * @param internalErrorIdentifier + * @param message + * @param paramErrorIdentifiers + * @return ErrorResponseObject + */ + protected ErrorResponseObject getParamErrorResponse(String internalErrorIdentifier, String message, + List paramErrorIdentifiers) { + + String paramsErrorPath = "classpath:config/ParamErrorConfig.json"; + Resource resource = resourceLoader.getResource(paramsErrorPath); + ObjectMapper objectMapper = new ObjectMapper(); + Map paramErrorDataMap = new HashMap<>(); + try { + paramErrorDataMap = objectMapper.readValue(resource.getInputStream(), + new TypeReference>() { + }); + } + catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException("Error while reading param error config file:" + e.getMessage()); + } + + List paramErrorConfigList = new ArrayList(); + + for (String paramErrorIdentifier : paramErrorIdentifiers) { + ParamErrorConfig paramErrorConfig = null; + + Pattern pattern = Pattern.compile("^missing_(.*)$"); + Matcher matcher = pattern.matcher(paramErrorIdentifier); + + if (matcher.matches()) { + String paramName = matcher.group(1); + String messageString = paramName + " is required parameter. Please provide " + paramName + "."; + + paramErrorConfig = new ParamErrorConfig(paramName, paramErrorIdentifier, messageString); + paramErrorConfigList.add(paramErrorConfig); + } + else { + paramErrorConfig = paramErrorDataMap.get(paramErrorIdentifier); + if (paramErrorConfig != null) { + paramErrorConfigList.add(paramErrorConfig); + } + } + } + + ErrorResponseObject errorResponseObject = new ErrorResponseObject(400, + "At least one parameter is invalid or missing.", "INVALID_PARAMS", internalErrorIdentifier, + paramErrorConfigList); + + return errorResponseObject; + } } diff --git a/src/main/java/com/salessparrow/api/exception/GlobalExceptionHandler.java b/src/main/java/com/salessparrow/api/exception/GlobalExceptionHandler.java index 9b87e84c..fe628eee 100644 --- a/src/main/java/com/salessparrow/api/exception/GlobalExceptionHandler.java +++ b/src/main/java/com/salessparrow/api/exception/GlobalExceptionHandler.java @@ -26,130 +26,110 @@ @ControllerAdvice public class GlobalExceptionHandler { - private Logger logger = org.slf4j.LoggerFactory.getLogger(GlobalExceptionHandler.class); - - @Autowired - private ErrorResponse er; - - @Autowired - private ErrorEmailService errorEmailService; - - @Autowired - private CookieHelper cookieHelper; - - /** - * Handle 404. Catches the exception for undefined endpoints - * - * @param NoHandlerFoundException - * - * @return ResponseEntity - */ - @ExceptionHandler(NoHandlerFoundException.class) - public ResponseEntity handleNoHandlerFoundException(NoHandlerFoundException ex, HttpServletRequest request) { - // Logging all headers from the request - String headerStr = Util.generateHeaderLogString(request); - logger.info("headerStr: {}", headerStr); - - ErrorResponseObject errorResponse = null; - - errorResponse = er.getErrorResponse( - "resource_not_found", - "a_e_geh_nf_1", - "handleNoHandlerFoundException"); - - return ResponseEntity.status(errorResponse.getHttpCode()) - .body(errorResponse); - } - - - /** - * Handle custom exception - * - * @param ex - * - * @return ResponseEntity> - */ - @ExceptionHandler(CustomException.class) - public ResponseEntity handleCustomException(CustomException ex) { - ErrorResponseObject errorResponse = null; - - if(ex.getParamErrorObject() != null){ - ParamErrorObject paramErrorObject = ex.getParamErrorObject(); - errorResponse = er.getParamErrorResponse( - paramErrorObject.getInternalErrorIdentifier(), - paramErrorObject.getMessage(), - paramErrorObject.getParamErrorIdentifiers()); - } else if (ex.getErrorObject() == null || ex.getErrorObject().getApiErrorIdentifier() == null) { - errorResponse = er.getErrorResponse( - "something_went_wrong", - "e_geh_hce_1", - ex.getMessage()); - } else { - ErrorObject errorObject = ex.getErrorObject(); - errorResponse = er.getErrorResponse( - errorObject.getApiErrorIdentifier(), - errorObject.getInternalErrorIdentifier(), - errorObject.getMessage()); - } - - logger.error("Error response: {}", errorResponse); - - // Send email for 500 errors only - if (errorResponse.getHttpCode() == 500) { - StackTraceElement[] stackTrace = ex.getStackTrace(); - errorEmailService.sendErrorMail("handleCustomException", errorResponse, stackTrace); - } - - // Clear user cookie for 401 errors - if (errorResponse.getHttpCode() == 401) { - HttpHeaders headers = new HttpHeaders(); - headers = cookieHelper.clearUserCookie(headers); - return ResponseEntity.status(errorResponse.getHttpCode()) - .headers(headers) - .body(errorResponse); - } - - return ResponseEntity.status(errorResponse.getHttpCode()) - .body(errorResponse); - } - - /** - * Catch-all exception handler for any unhandled runtime exception - * - * @param ex - * - * @return ResponseEntity - */ - @ExceptionHandler(RuntimeException.class) - @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) - public ResponseEntity handleRuntimeException(RuntimeException ex) { - ex.printStackTrace(); - - ErrorResponseObject errorResponse = er.getErrorResponse("something_went_wrong", - "e_geh_hre_1", ex.getMessage()); - - StackTraceElement[] stackTrace = ex.getStackTrace(); - errorEmailService.sendErrorMail("RuntimeException", errorResponse, stackTrace); - - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) - .body(errorResponse); - } - - @ExceptionHandler(MethodArgumentNotValidException.class) - @ResponseStatus(HttpStatus.BAD_REQUEST) - public ResponseEntity handleValidationException(MethodArgumentNotValidException ex) { - List paramErrorIdentifiers = new ArrayList<>(); - - for (FieldError error : ex.getBindingResult().getFieldErrors()) { - paramErrorIdentifiers.add(error.getDefaultMessage()); - } - - CustomException ce2 = new CustomException( - new ParamErrorObject( - "b_2", - paramErrorIdentifiers.toString(), - paramErrorIdentifiers)); - - return handleCustomException(ce2); - } + private Logger logger = org.slf4j.LoggerFactory.getLogger(GlobalExceptionHandler.class); + + @Autowired + private ErrorResponse er; + + @Autowired + private ErrorEmailService errorEmailService; + + @Autowired + private CookieHelper cookieHelper; + + /** + * Handle 404. Catches the exception for undefined endpoints + * @param NoHandlerFoundException + * @return ResponseEntity + */ + @ExceptionHandler(NoHandlerFoundException.class) + public ResponseEntity handleNoHandlerFoundException(NoHandlerFoundException ex, + HttpServletRequest request) { + // Logging all headers from the request + String headerStr = Util.generateHeaderLogString(request); + logger.info("headerStr: {}", headerStr); + + ErrorResponseObject errorResponse = null; + + errorResponse = er.getErrorResponse("resource_not_found", "a_e_geh_nf_1", "handleNoHandlerFoundException"); + + return ResponseEntity.status(errorResponse.getHttpCode()).body(errorResponse); + } + + /** + * Handle custom exception + * @param ex + * @return ResponseEntity> + */ + @ExceptionHandler(CustomException.class) + public ResponseEntity handleCustomException(CustomException ex) { + ErrorResponseObject errorResponse = null; + + if (ex.getParamErrorObject() != null) { + ParamErrorObject paramErrorObject = ex.getParamErrorObject(); + logger.error("Message from custom exception: {}", paramErrorObject.getMessage()); + errorResponse = er.getParamErrorResponse(paramErrorObject.getInternalErrorIdentifier(), + paramErrorObject.getMessage(), paramErrorObject.getParamErrorIdentifiers()); + } + else if (ex.getErrorObject() == null || ex.getErrorObject().getApiErrorIdentifier() == null) { + errorResponse = er.getErrorResponse("something_went_wrong", "e_geh_hce_1", ex.getMessage()); + } + else { + ErrorObject errorObject = ex.getErrorObject(); + logger.error("Message from custom exception: {}", errorObject.getMessage()); + errorResponse = er.getErrorResponse(errorObject.getApiErrorIdentifier(), + errorObject.getInternalErrorIdentifier(), errorObject.getMessage()); + } + + logger.error("Error response: {}", errorResponse); + + // Send email for 500 errors only + if (errorResponse.getHttpCode() == 500) { + StackTraceElement[] stackTrace = ex.getStackTrace(); + errorEmailService.sendErrorMail("handleCustomException", errorResponse, stackTrace); + } + + // Clear user cookie for 401 errors + if (errorResponse.getHttpCode() == 401) { + HttpHeaders headers = new HttpHeaders(); + headers = cookieHelper.clearUserCookie(headers); + return ResponseEntity.status(errorResponse.getHttpCode()).headers(headers).body(errorResponse); + } + + return ResponseEntity.status(errorResponse.getHttpCode()).body(errorResponse); + } + + /** + * Catch-all exception handler for any unhandled runtime exception + * @param ex + * @return ResponseEntity + */ + @ExceptionHandler(RuntimeException.class) + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) + public ResponseEntity handleRuntimeException(RuntimeException ex) { + ex.printStackTrace(); + + ErrorResponseObject errorResponse = er.getErrorResponse("something_went_wrong", "e_geh_hre_1", ex.getMessage()); + + StackTraceElement[] stackTrace = ex.getStackTrace(); + errorEmailService.sendErrorMail("RuntimeException", errorResponse, stackTrace); + + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorResponse); + } + + @ExceptionHandler(MethodArgumentNotValidException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public ResponseEntity handleValidationException(MethodArgumentNotValidException ex) { + List paramErrorIdentifiers = new ArrayList<>(); + + for (FieldError error : ex.getBindingResult().getFieldErrors()) { + paramErrorIdentifiers.add(error.getDefaultMessage()); + } + + CustomException ce2 = new CustomException( + new ParamErrorObject("b_2", paramErrorIdentifiers.toString(), paramErrorIdentifiers)); + + return handleCustomException(ce2); + } + } \ No newline at end of file diff --git a/src/main/java/com/salessparrow/api/filter/SameSiteCookieFilter.java b/src/main/java/com/salessparrow/api/filter/SameSiteCookieFilter.java new file mode 100644 index 00000000..518ffb28 --- /dev/null +++ b/src/main/java/com/salessparrow/api/filter/SameSiteCookieFilter.java @@ -0,0 +1,96 @@ +package com.salessparrow.api.filter; + +import jakarta.servlet.*; +import jakarta.servlet.http.*; + +import java.io.IOException; + +/** + * Filter to set SameSite cookie attribute + * + * If you want to implement SameSite attribute setting in an interceptor, you can do it, + * but it won't be as efficient as doing it in a filter. That's because, by the time the + * interceptor is called, some part of the request processing within the Spring MVC + * framework has already happened. + * + */ +public class SameSiteCookieFilter implements Filter { + + @Override + public void init(FilterConfig filterConfig) { + } + + /** + * Set SameSite cookie attribute. Override the doFilter method to wrap the response + * object with a custom wrapper. + * @param request + * @param response + * @param chain + * @throws IOException + * @throws ServletException + * @return void + */ + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException { + + SameSiteResponseWrapper wrappedResponse = new SameSiteResponseWrapper((HttpServletResponse) response); + chain.doFilter(request, wrappedResponse); + } + + @Override + public void destroy() { + } + + /** + * Custom wrapper to intercept the setHeader calls and add SameSite attribute to the + * cookie. Set SameSite=lax lax will ensure that cookies are sent only if the request + * originates from the same site. It will also send the cookie if the user is + * navigating from an external site, but only if the URL in the browser’s address bar + * matches the URL of the current site. + */ + public class SameSiteResponseWrapper extends HttpServletResponseWrapper { + + /** + * Constructor + * @param response + * @return void + */ + public SameSiteResponseWrapper(HttpServletResponse response) { + super(response); + } + + /** + * Override the addHeader method to add SameSite attribute to the cookie + * @param name + * @param value + * @return void + * + */ + @Override + public void addHeader(String name, String value) { + if (name.equalsIgnoreCase("set-cookie") && !value.toLowerCase().contains("samesite")) { + value = String.format("%s; SameSite=lax", value); + } + super.addHeader(name, value); + } + + /** + * Override the setHeader method to add SameSite attribute to the cookie + * @param name + * @param value + * @return void + * + */ + @Override + public void setHeader(String name, String value) { + + if (name.equalsIgnoreCase("set-cookie") && !value.toLowerCase().contains("samesite")) { + value = String.format("%s; SameSite=lax", value); + } + super.setHeader(name, value); + } + + } + +} diff --git a/src/main/java/com/salessparrow/api/filter/SanitizationFilter.java b/src/main/java/com/salessparrow/api/filter/SanitizationFilter.java new file mode 100644 index 00000000..69276f88 --- /dev/null +++ b/src/main/java/com/salessparrow/api/filter/SanitizationFilter.java @@ -0,0 +1,159 @@ +package com.salessparrow.api.filter; + +import jakarta.servlet.Filter; +import jakarta.servlet.FilterChain; +import jakarta.servlet.FilterConfig; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.safety.Safelist; +import org.jsoup.nodes.Entities.EscapeMode; + +import com.salessparrow.api.lib.wrappers.SanitizedRequestWrapper; + +import java.io.BufferedReader; +import java.io.IOException; +import java.util.Enumeration; + +/** + * Class to sanitize the request + */ +public class SanitizationFilter implements Filter { + + @Override + public void init(FilterConfig filterConfig) { + } + + /** + * Method to sanitize the request + * @param servletRequest - Servlet request object + * @param servletResponse - Servlet response object + * @param chain - Filter chain - Filter chain + * @throws IOException - IOException + * @throws ServletException - ServletException + * @return void + */ + @Override + public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) + throws IOException, ServletException { + if (!(servletRequest instanceof HttpServletRequest)) { + throw new ServletException("Can only process HttpServletRequest"); + } + + HttpServletRequest request = (HttpServletRequest) servletRequest; + SanitizedRequestWrapper sanitizedRequest = sanitizeRequestBody(request); + sanitizeRequestParams(request, sanitizedRequest); + sanitizeRequestHeaders(request, sanitizedRequest); + + chain.doFilter(sanitizedRequest, servletResponse); + } + + /** + * Method to sanitize the request body + * @param request - Servlet request object + * @return void + */ + private SanitizedRequestWrapper sanitizeRequestBody(HttpServletRequest request) { + String originalBody = getRequestBody(request); + String sanitizedBody = sanitizeHtml(originalBody); + + return new SanitizedRequestWrapper(request, sanitizedBody); + } + + /** + * Method to sanitize the request params + * @return void + */ + private void sanitizeRequestParams(HttpServletRequest request, SanitizedRequestWrapper sanitizedRequest) { + request.getParameterMap().forEach((key, values) -> { + for (int index = 0; index < values.length; index++) { + String sanitizedValue = sanitizeHtml(values[index]); + sanitizedRequest.setParameter(key, sanitizedValue); + } + }); + } + + /** + * Method to sanitize the request headers + * @return void + */ + private void sanitizeRequestHeaders(HttpServletRequest request, SanitizedRequestWrapper sanitizedRequest) { + Enumeration headerNames = request.getHeaderNames(); + + while (headerNames != null && headerNames.hasMoreElements()) { + String headerName = headerNames.nextElement(); + Enumeration headerValues = request.getHeaders(headerName); + + while (headerValues != null && headerValues.hasMoreElements()) { + String headerValue = headerValues.nextElement(); + String sanitizedValue = sanitizeHtml(headerValue); + sanitizedRequest.setHeader(headerName, sanitizedValue); + } + } + } + + /** + * Method to get the request body + * @param request - Servlet request object + * @return String - Request body + */ + private String getRequestBody(HttpServletRequest request) { + try { + BufferedReader reader = request.getReader(); + String line; + // TODO: Consider using a more efficient way to read the request body, such as + // streaming + StringBuilder requestBody = new StringBuilder(); + while ((line = reader.readLine()) != null) { + requestBody.append(line); + } + return requestBody.toString(); + } + catch (IOException e) { + throw new RuntimeException("Error reading request body: ", e.getCause()); + } + } + + /** + * Sanitizes a given HTML string by removing all HTML tags and attributes, leaving + * only the text content. Also handles basic HTML entity escaping using the 'base' + * escape mode. + * + * This function uses the Jsoup library's 'clean' method with a 'none' safelist, which + * means no HTML tags are allowed and will be removed. + * + * The function also disables pretty-printing to ensure the resulting HTML is not + * reformatted, and sets the escape mode to 'base' to handle basic HTML entities like + * & and <. + * + * If the input is null or empty, the function returns an empty string. + * @param input The original HTML string that needs to be sanitized. + * @return A sanitized version of the input string, safe for display as text. + */ + private String sanitizeHtml(String input) { + if (input == null || input.isEmpty()) { + return ""; + } + + Safelist safelist = Safelist.none(); + + Document.OutputSettings outputSettings = new Document.OutputSettings(); + outputSettings.prettyPrint(false); + outputSettings.escapeMode(EscapeMode.base); + + String sanitizedInput = Jsoup.clean(input, "", safelist, outputSettings); + + return sanitizedInput; + } + + /** + * Method to destroy the filter + */ + @Override + public void destroy() { + } + +} diff --git a/src/main/java/com/salessparrow/api/interceptors/JsonOnlyInterceptor.java b/src/main/java/com/salessparrow/api/interceptors/JsonOnlyInterceptor.java new file mode 100644 index 00000000..fb72023f --- /dev/null +++ b/src/main/java/com/salessparrow/api/interceptors/JsonOnlyInterceptor.java @@ -0,0 +1,39 @@ +package com.salessparrow.api.interceptors; + +import org.springframework.web.servlet.HandlerInterceptor; + +import com.salessparrow.api.exception.CustomException; +import com.salessparrow.api.lib.errorLib.ErrorObject; + +import org.springframework.stereotype.Component; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +/** + * Interceptor for strict application/json Content-Type header + * + */ +@Component +public class JsonOnlyInterceptor implements HandlerInterceptor { + + /** + * Intercept request and validate Content-type header Only application/json is allowed + * @param request + * @param response + * @param handler + * @return boolean + */ + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) + throws Exception { + + String contentType = request.getHeader("Content-Type"); + if (contentType != null && !contentType.contains("application/json")) { + throw new CustomException(new ErrorObject("i_joi_ph_1", "unsupported_media_type", + "Content-Type header must be application/json")); + } + + return true; + } + +} diff --git a/src/main/java/com/salessparrow/api/interceptors/LoggerInterceptor.java b/src/main/java/com/salessparrow/api/interceptors/LoggerInterceptor.java index 37f1ccda..94233663 100644 --- a/src/main/java/com/salessparrow/api/interceptors/LoggerInterceptor.java +++ b/src/main/java/com/salessparrow/api/interceptors/LoggerInterceptor.java @@ -18,46 +18,47 @@ @Component public class LoggerInterceptor implements HandlerInterceptor { - /** - * Intercept request and log request - * - * @param request - * @param response - * @param handler - * - * @return boolean - */ - @Override - public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { - String trackingId = request.getHeader("X-Tracking-Id"); - - Logger logger = LoggerFactory.getLogger(LoggerInterceptor.class); - - if (trackingId == null) { - trackingId = java.util.UUID.randomUUID().toString(); - } - - long startTime = System.currentTimeMillis(); - request.setAttribute("startTime", startTime); - - MDC.put("trackingId", trackingId); - logger.info("Request Start: {} {}", request.getMethod(), request.getRequestURI()); - - // Logging all headers from the request - String headerStr = Util.generateHeaderLogString(request); - logger.info("headerStr: {}", headerStr); - - return true; - } - - @Override - public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { - Logger logger = LoggerFactory.getLogger(LoggerInterceptor.class); - - long startTime = (Long)request.getAttribute("startTime"); - long endTime = System.currentTimeMillis(); - long executeTime = endTime - startTime; - - logger.info("Request Ended with {} in {}ms", response.getStatus(), executeTime); - } + /** + * Intercept request and log request + * @param request + * @param response + * @param handler + * @return boolean + */ + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) + throws Exception { + String trackingId = request.getHeader("X-Tracking-Id"); + + Logger logger = LoggerFactory.getLogger(LoggerInterceptor.class); + + if (trackingId == null) { + trackingId = java.util.UUID.randomUUID().toString(); + } + + long startTime = System.currentTimeMillis(); + request.setAttribute("startTime", startTime); + + MDC.put("trackingId", trackingId); + logger.info("Request Start: {} {}", request.getMethod(), request.getRequestURI()); + + // Logging all headers from the request + String headerStr = Util.generateHeaderLogString(request); + logger.info("headerStr: {}", headerStr); + + return true; + } + + @Override + public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) + throws Exception { + Logger logger = LoggerFactory.getLogger(LoggerInterceptor.class); + + long startTime = (Long) request.getAttribute("startTime"); + long endTime = System.currentTimeMillis(); + long executeTime = endTime - startTime; + + logger.info("Request Ended with {} in {}ms", response.getStatus(), executeTime); + } + } diff --git a/src/main/java/com/salessparrow/api/interceptors/UserAuthInterceptor.java b/src/main/java/com/salessparrow/api/interceptors/UserAuthInterceptor.java index be4b72d0..e03e3ab7 100644 --- a/src/main/java/com/salessparrow/api/interceptors/UserAuthInterceptor.java +++ b/src/main/java/com/salessparrow/api/interceptors/UserAuthInterceptor.java @@ -25,62 +25,61 @@ @Component public class UserAuthInterceptor implements HandlerInterceptor { - @Autowired - private UserLoginCookieAuth userLoginCookieAuth; - - Logger logger = LoggerFactory.getLogger(UserAuthInterceptor.class); - - @Autowired - private CookieHelper cookieHelper; - - /** - * Intercept request and validate user login cookie - * - * @param request - * @param response - * @param handler - * - * @return boolean - */ - @Override - public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { - String cookieValue = getCookieValue(request); - - Map userLoginCookieAuthRes = userLoginCookieAuth.validateAndSetCookie(cookieValue); - - User currentUser = (User) userLoginCookieAuthRes.get("current_user"); - request.setAttribute("current_user", currentUser); - - String userLoginCookieValue = (String) userLoginCookieAuthRes.get("userLoginCookieValue"); - - String cookieName = CookieConstants.USER_LOGIN_COOKIE_NAME; - - HttpHeaders headers = new HttpHeaders(); - headers = cookieHelper.setCookieInHeaders(cookieName, userLoginCookieValue, headers); - - response.addHeader(HttpHeaders.SET_COOKIE, headers.getFirst(HttpHeaders.SET_COOKIE)); - - return true; - } - - /** - * Get cookie value from request - * - * @param request - * @param cookieName - * - * @return String - */ - private String getCookieValue(HttpServletRequest request) { - Cookie[] cookies = request.getCookies(); - if (cookies != null) { - for (Cookie cookie : cookies) { - if (cookie.getName().equals(CookieConstants.USER_LOGIN_COOKIE_NAME)) { - return cookie.getValue(); - } - } - } - return null; - } + @Autowired + private UserLoginCookieAuth userLoginCookieAuth; + + Logger logger = LoggerFactory.getLogger(UserAuthInterceptor.class); + + @Autowired + private CookieHelper cookieHelper; + + /** + * Intercept request and validate user login cookie + * @param request + * @param response + * @param handler + * @return boolean + */ + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) + throws Exception { + String cookieValue = getCookieValue(request); + String reqApiSource = (String) request.getAttribute("api_source"); + + Map userLoginCookieAuthRes = userLoginCookieAuth.validateAndSetCookie(cookieValue, + reqApiSource); + + User currentUser = (User) userLoginCookieAuthRes.get("current_user"); + request.setAttribute("current_user", currentUser); + + String userLoginCookieValue = (String) userLoginCookieAuthRes.get("userLoginCookieValue"); + + String cookieName = CookieConstants.USER_LOGIN_COOKIE_NAME; + + HttpHeaders headers = new HttpHeaders(); + headers = cookieHelper.setCookieInHeaders(cookieName, userLoginCookieValue, headers); + + response.addHeader(HttpHeaders.SET_COOKIE, headers.getFirst(HttpHeaders.SET_COOKIE)); + + return true; + } + + /** + * Get cookie value from request + * @param request + * @param cookieName + * @return String + */ + private String getCookieValue(HttpServletRequest request) { + Cookie[] cookies = request.getCookies(); + if (cookies != null) { + for (Cookie cookie : cookies) { + if (cookie.getName().equals(CookieConstants.USER_LOGIN_COOKIE_NAME)) { + return cookie.getValue(); + } + } + } + return null; + } } diff --git a/src/main/java/com/salessparrow/api/interceptors/V1Interceptor.java b/src/main/java/com/salessparrow/api/interceptors/V1Interceptor.java index 4a6876b3..4c7940e1 100644 --- a/src/main/java/com/salessparrow/api/interceptors/V1Interceptor.java +++ b/src/main/java/com/salessparrow/api/interceptors/V1Interceptor.java @@ -14,20 +14,21 @@ @Component public class V1Interceptor implements HandlerInterceptor { - /** - * Intercept all /v1/* request and add middleware logic here - * - * @param request - * @param response - * @param handler - * - * @return boolean - */ - @Override - public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + /** + * Intercept all /v1/* request and add middleware logic here + * @param request + * @param response + * @param handler + * @return boolean + */ + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) + throws Exception { + + // Set Source for app.This can be used to differentiate common web routes from app + // routes in the service. + request.setAttribute("api_source", ApiSource.APP); + return true; + } - // Set Source for app.This can be used to differentiate common web routes from app routes in the service. - request.setAttribute("api_source", ApiSource.APP); - return true; - } } diff --git a/src/main/java/com/salessparrow/api/lib/AwsKms.java b/src/main/java/com/salessparrow/api/lib/AwsKms.java index 527b4bf3..8b8b7248 100644 --- a/src/main/java/com/salessparrow/api/lib/AwsKms.java +++ b/src/main/java/com/salessparrow/api/lib/AwsKms.java @@ -16,79 +16,67 @@ import com.salessparrow.api.lib.errorLib.ErrorObject; /** - * AWS Key Management Service (KMS) component for encryption and decryption - * operations. + * AWS Key Management Service (KMS) component for encryption and decryption operations. */ @Component public class AwsKms { - @Autowired - private AWSKMS kmsClient; + @Autowired + private AWSKMS kmsClient; - /** - * Creates a new instance of AwsKms. - */ - public AwsKms() { - } + /** + * Creates a new instance of AwsKms. + */ + public AwsKms() { + } - /** - * Encrypts a token using AWS KMS. - * - * @param token - * - * @return The encrypted token. - */ - public String encryptToken(String token) { - if (token == null) { - return null; - } - EncryptRequest request = new EncryptRequest() - .withKeyId(CoreConstants.kmsKeyId()) - .withPlaintext(ByteBuffer.wrap(token.getBytes())); + /** + * Encrypts a token using AWS KMS. + * @param token + * @return The encrypted token. + */ + public String encryptToken(String token) { + if (token == null) { + return null; + } + EncryptRequest request = new EncryptRequest().withKeyId(CoreConstants.kmsKeyId()) + .withPlaintext(ByteBuffer.wrap(token.getBytes())); - EncryptResult result = null; - try { - result = kmsClient.encrypt(request); - } catch (Exception e) { - throw new CustomException( - new ErrorObject( - "l_ak_et_1", - "something_went_wrong", - e.getMessage())); - } - return Base64.getEncoder().encodeToString(result.getCiphertextBlob().array()); + EncryptResult result = null; + try { + result = kmsClient.encrypt(request); + } + catch (Exception e) { + throw new CustomException(new ErrorObject("l_ak_et_1", "something_went_wrong", e.getMessage())); + } + return Base64.getEncoder().encodeToString(result.getCiphertextBlob().array()); - } + } - /** - * Decrypts a token using AWS KMS. - * - * @param encryptedToken - * - * @return The decrypted token. - */ - public String decryptToken(String encryptedToken) { - byte[] decodedToken = Base64.getDecoder().decode(encryptedToken); + /** + * Decrypts a token using AWS KMS. + * @param encryptedToken + * @return The decrypted token. + */ + public String decryptToken(String encryptedToken) { + byte[] decodedToken = Base64.getDecoder().decode(encryptedToken); - DecryptRequest request = new DecryptRequest() - .withCiphertextBlob(ByteBuffer.wrap(decodedToken)) - .withKeyId(CoreConstants.kmsKeyId()); - DecryptResult result = null; + DecryptRequest request = new DecryptRequest().withCiphertextBlob(ByteBuffer.wrap(decodedToken)) + .withKeyId(CoreConstants.kmsKeyId()); + DecryptResult result = null; - try { - result = kmsClient.decrypt(request); - } catch (Exception e) { - throw new CustomException( - new ErrorObject( - "l_ak_dt_1", - "something_went_wrong", - e.getMessage())); - } + try { + result = kmsClient.decrypt(request); + } + catch (Exception e) { + throw new CustomException(new ErrorObject("l_ak_dt_1", "something_went_wrong", e.getMessage())); + } - if (result == null || result.getPlaintext() == null) { - return null; - } + if (result == null || result.getPlaintext() == null) { + return null; + } + + return new String(result.getPlaintext().array()); + } - return new String(result.getPlaintext().array()); - } } \ No newline at end of file diff --git a/src/main/java/com/salessparrow/api/lib/Base64Helper.java b/src/main/java/com/salessparrow/api/lib/Base64Helper.java index 945f06b2..3478a0e5 100644 --- a/src/main/java/com/salessparrow/api/lib/Base64Helper.java +++ b/src/main/java/com/salessparrow/api/lib/Base64Helper.java @@ -6,15 +6,17 @@ @Component public class Base64Helper { - public String base64Encode(String originalString) { - byte[] originalBytes = originalString.getBytes(); - byte[] encodedBytes = Base64.getEncoder().encode(originalBytes); - return new String(encodedBytes); - } - public String base64Decode(String encodedString) { - byte[] encodedBytes = encodedString.getBytes(); - byte[] decodedBytes = Base64.getDecoder().decode(encodedBytes); - return new String(decodedBytes); - } + public String base64Encode(String originalString) { + byte[] originalBytes = originalString.getBytes(); + byte[] encodedBytes = Base64.getEncoder().encode(originalBytes); + return new String(encodedBytes); + } + + public String base64Decode(String encodedString) { + byte[] encodedBytes = encodedString.getBytes(); + byte[] decodedBytes = Base64.getDecoder().decode(encodedBytes); + return new String(decodedBytes); + } + } diff --git a/src/main/java/com/salessparrow/api/lib/CookieHelper.java b/src/main/java/com/salessparrow/api/lib/CookieHelper.java index 5ddc9933..06275ce8 100644 --- a/src/main/java/com/salessparrow/api/lib/CookieHelper.java +++ b/src/main/java/com/salessparrow/api/lib/CookieHelper.java @@ -15,72 +15,103 @@ @Component public class CookieHelper { - @Autowired - private LocalCipher localCipher; + @Autowired + private LocalCipher localCipher; + + /** + * Get cookie value + * @param user + * @param userKind + * @param decryptedSalt + * @param apiSource + * @return String + */ + public String getCookieValue(User user, String userKind, String decryptedSalt, String apiSource) { + + Integer currentTimestamp = (int) (System.currentTimeMillis() / 1000); + String cookieToken = getCookieToken(user, decryptedSalt, currentTimestamp, apiSource); + + if (user.getExternalUserId() == null) { + throw new CustomException(new ErrorObject("l_ch_gcv_1", "internal_server_error", "User is null")); + } + + return CookieConstants.LATEST_VERSION + ':' + user.getExternalUserId() + ':' + userKind + ':' + apiSource + ':' + + currentTimestamp + ':' + cookieToken; + } + + /** + * Get cookie token + * @param user + * @param decryptedSalt + * @param timestamp + * @param apiSource + * @return String + */ + public String getCookieToken(User user, String decryptedSalt, Integer timestamp, String apiSource) { + + String decryptedCookieToken = localCipher.decrypt(decryptedSalt, user.getCookieToken()); + String strSecret = CoreConstants.apiCookieSecret(); + String stringToSign = user.getExternalUserId() + ':' + timestamp + ':' + apiSource + ':' + strSecret + ':' + + decryptedCookieToken.substring(0, 16); + String salt = user.getExternalUserId() + ':' + + decryptedCookieToken.substring(decryptedCookieToken.length() - 16) + ':' + strSecret + ':' + + timestamp; + String encryptedCookieToken = localCipher.encrypt(salt, stringToSign); + + return encryptedCookieToken; + } + + /** + * Set cookie in headers + * @param cookieName + * @param cookieValue + * @param headers + * @return HttpHeaders + */ + public HttpHeaders setCookieInHeaders(String cookieName, String cookieValue, HttpHeaders headers) { + int cookieExpiryInSecond = CookieConstants.USER_LOGIN_COOKIE_EXPIRY_IN_SEC; + + Cookie cookie = new Cookie(cookieName, cookieValue); + cookie.setHttpOnly(true); + cookie.setSecure(true); + cookie.setMaxAge(cookieExpiryInSecond); + cookie.setDomain(CoreConstants.cookieDomain()); + cookie.setPath("/"); + + headers.add(HttpHeaders.SET_COOKIE, + String.format("%s=%s; Max-Age=%d; Path=/", cookieName, cookieValue, cookieExpiryInSecond)); + + return headers; + } + + /** + * Set user cookie + * @param cookieValue + * @param headers + * @return HttpHeaders + */ + public HttpHeaders setUserCookie(String cookieValue, HttpHeaders headers) { + String cookieName = CookieConstants.USER_LOGIN_COOKIE_NAME; + + headers = setCookieInHeaders(cookieName, cookieValue, headers); + + return headers; + } + + /** + * Clear user cookie + * @param headers + * @return HttpHeaders + */ + public HttpHeaders clearUserCookie(HttpHeaders headers) { + String cookieName = CookieConstants.USER_LOGIN_COOKIE_NAME; + String cookieValue = ""; + int cookieExpiry = -1; + + headers.add(HttpHeaders.SET_COOKIE, + String.format("%s=%s; Max-Age=%d; Path=/", cookieName, cookieValue, cookieExpiry)); + + return headers; + } - public String getCookieValue(User user, String userKind, String decryptedSalt) { - - Integer currentTimestamp = (int) (System.currentTimeMillis() / 1000); - String cookieToken = getCookieToken(user, decryptedSalt, currentTimestamp); - - if (user.getExternalUserId() == null) { - throw new CustomException( - new ErrorObject( - "l_ch_gcv_1", - "internal_server_error", - "User is null")); - } - - return CookieConstants.LATEST_VERSION + ':' + user.getExternalUserId() + ':' + userKind + ':' + currentTimestamp - + ':' - + cookieToken; - } - - public String getCookieToken(User user, String decryptedSalt, Integer timestamp) { - - String decryptedCookieToken = localCipher.decrypt(decryptedSalt, user.getCookieToken()); - String strSecret = CoreConstants.apiCookieSecret(); - String stringToSign = user.getExternalUserId() + ':' + timestamp + ':' + strSecret + ':' - + decryptedCookieToken.substring(0, 16); - String salt = user.getExternalUserId() + ':' + decryptedCookieToken.substring(decryptedCookieToken.length() - 16) - + ':' - + strSecret + ':' + timestamp; - String encryptedCookieToken = localCipher.encrypt(salt, stringToSign); - - return encryptedCookieToken; - } - - public HttpHeaders setCookieInHeaders(String cookieName, String cookieValue, - HttpHeaders headers) { - int cookieExpiryInSecond = CookieConstants.USER_LOGIN_COOKIE_EXPIRY_IN_SEC; - - Cookie cookie = new Cookie(cookieName, cookieValue); - cookie.setHttpOnly(true); - cookie.setSecure(true); - cookie.setMaxAge(cookieExpiryInSecond); - - headers.add(HttpHeaders.SET_COOKIE, String.format("%s=%s; Max-Age=%d; Path=/", - cookieName, cookieValue, cookieExpiryInSecond)); - - return headers; - } - - public HttpHeaders setUserCookie(String cookieValue, HttpHeaders headers) { - String cookieName = CookieConstants.USER_LOGIN_COOKIE_NAME; - - headers = setCookieInHeaders(cookieName, cookieValue, headers); - - return headers; - } - - public HttpHeaders clearUserCookie(HttpHeaders headers) { - String cookieName = CookieConstants.USER_LOGIN_COOKIE_NAME; - String cookieValue = ""; - int cookieExpiry = -1; - - headers.add(HttpHeaders.SET_COOKIE, String.format("%s=%s; Max-Age=%d; Path=/", - cookieName, cookieValue, cookieExpiry)); - - return headers; - } } diff --git a/src/main/java/com/salessparrow/api/lib/ErrorEmailService.java b/src/main/java/com/salessparrow/api/lib/ErrorEmailService.java index 7d523779..29236e02 100644 --- a/src/main/java/com/salessparrow/api/lib/ErrorEmailService.java +++ b/src/main/java/com/salessparrow/api/lib/ErrorEmailService.java @@ -15,75 +15,74 @@ @Component public class ErrorEmailService { - Logger logger = LoggerFactory.getLogger(ErrorEmailService.class); + Logger logger = LoggerFactory.getLogger(ErrorEmailService.class); - @Autowired - private AmazonSimpleEmailService sesClient; + @Autowired + private AmazonSimpleEmailService sesClient; - /** - * Send error email - * - * @param contextString - context string for subject. helps in easy - * identification of error - * @param errorObj - error object - contains error code, message, http - * code, internal error identifier - * @param stackTraceElements - stack trace elements - * - * @return void - */ - public void sendErrorMail(String contextString, ErrorResponseObject errorObj, - StackTraceElement[] stackTraceElements) { - logger.info("Sending error email for context: " + contextString); + /** + * Send error email + * @param contextString - context string for subject. helps in easy identification of + * error + * @param errorObj - error object - contains error code, message, http code, internal + * error identifier + * @param stackTraceElements - stack trace elements + * @return void + */ + public void sendErrorMail(String contextString, ErrorResponseObject errorObj, + StackTraceElement[] stackTraceElements) { + logger.info("Sending error email for context: " + contextString); - String requestId = MDC.get("trackingId"); + String requestId = MDC.get("trackingId"); - String body = "Tracking id:" + requestId + "\n"; - body += "http_code=" + errorObj.getHttpCode() + "\n"; - body += "code=" + errorObj.getCode() + "\n"; - body += "message=" + errorObj.getMessage() + "\n"; - body += "internal_error_identifier=" + errorObj.getInternalErrorIdentifier() + "\n"; - body += "params_error=" + errorObj.getParamErrors() + "\n\n\n"; + String body = "Tracking id:" + requestId + "\n"; + body += "http_code=" + errorObj.getHttpCode() + "\n"; + body += "code=" + errorObj.getCode() + "\n"; + body += "message=" + errorObj.getMessage() + "\n"; + body += "internal_error_identifier=" + errorObj.getInternalErrorIdentifier() + "\n"; + body += "params_error=" + errorObj.getParamErrors() + "\n\n\n"; - for (StackTraceElement stackTraceElement : stackTraceElements) { - body += stackTraceElement.toString() + "\n"; - } + for (StackTraceElement stackTraceElement : stackTraceElements) { + body += stackTraceElement.toString() + "\n"; + } - String subject = "SalesSparrow::" + CoreConstants.environment() + "::Error-" + contextString - + "-" + errorObj.getMessage(); + String subject = "SalesSparrow::" + CoreConstants.environment() + "::Error-" + contextString + "-" + + errorObj.getMessage(); - // Send email only if not in dev environment - if (!CoreConstants.isDevEnvironment() && !CoreConstants.isTestEnvironment()) { - sendEmail(CoreConstants.errorEmailFrom(), CoreConstants.errorEmailTo(), subject, body); - } else { - logger.info("Skip email for development.\n\n subject {} \n body {}", subject, body); - } + // Send email only if not in dev environment + if (!CoreConstants.isDevEnvironment() && !CoreConstants.isTestEnvironment() + && !CoreConstants.isLocalTestEnvironment()) { + sendEmail(CoreConstants.errorEmailFrom(), CoreConstants.errorEmailTo(), subject, body); + } + else { + logger.info("Skip email for development.\n\n subject {} \n body {}", subject, body); + } - } + } - /** - * Send mail async using SES. Handles exception if any - * - * @param from - source email address - * @param to - destination email address - * @param subject - subject of email - * @param body - body of email - * - * @return void - */ - @Async - public void sendEmail(String from, String to, String subject, String body) { - logger.info("send SES Email"); - try { - SendEmailRequest request = new SendEmailRequest() - .withDestination(new Destination().withToAddresses(to)) - .withMessage(new Message() - .withBody(new Body().withText(new Content().withCharset("UTF-8").withData(body))) - .withSubject(new Content().withCharset("UTF-8").withData(subject))) - .withSource(from); + /** + * Send mail async using SES. Handles exception if any + * @param from - source email address + * @param to - destination email address + * @param subject - subject of email + * @param body - body of email + * @return void + */ + @Async + public void sendEmail(String from, String to, String subject, String body) { + logger.info("send SES Email"); + try { + SendEmailRequest request = new SendEmailRequest().withDestination(new Destination().withToAddresses(to)) + .withMessage( + new Message().withBody(new Body().withText(new Content().withCharset("UTF-8").withData(body))) + .withSubject(new Content().withCharset("UTF-8").withData(subject))) + .withSource(from); + + sesClient.sendEmail(request); + } + catch (Exception e) { + logger.error("Error sending email {} subject:{} body:{}", e, subject, body); + } + } - sesClient.sendEmail(request); - } catch (Exception e) { - logger.error("Error sending email {} subject:{} body:{}", e, subject, body); - } - } } diff --git a/src/main/java/com/salessparrow/api/lib/GetCrmActionSuggestions.java b/src/main/java/com/salessparrow/api/lib/GetCrmActionSuggestions.java new file mode 100644 index 00000000..d28da757 --- /dev/null +++ b/src/main/java/com/salessparrow/api/lib/GetCrmActionSuggestions.java @@ -0,0 +1,124 @@ +package com.salessparrow.api.lib; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.slf4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.salessparrow.api.controllers.SuggestionsController; +import com.salessparrow.api.dto.entities.AddTaskSuggestionEntityDto; +import com.salessparrow.api.dto.formatter.CrmActionSuggestionsFormatterDto; +import com.salessparrow.api.exception.CustomException; +import com.salessparrow.api.lib.errorLib.ErrorObject; +import com.salessparrow.api.lib.openAi.OpenAiPayloadBuilder; +import com.salessparrow.api.lib.openAi.OpenAiRequest; +import com.salessparrow.api.lib.validators.DateFormatValidator; + +/** + * GetCrmActionSuggestions is a class for getting the crm action suggestions. + */ +@Component +public class GetCrmActionSuggestions { + + @Autowired + private OpenAiRequest openAiRequest; + + @Autowired + private OpenAiPayloadBuilder openAiPayloadBuilder; + + private DateFormatValidator dateFormatValidator = new DateFormatValidator(); + + private Logger logger = org.slf4j.LoggerFactory.getLogger(SuggestionsController.class); + + /** + * Get the crm action suggestions. + * @param text + * @return + */ + public CrmActionSuggestionsFormatterDto getTaskSuggestions(String text) { + logger.info("Crm actions suggestions lib called for text: " + text); + + String escapedText = escapeForJson(text); + String payload = openAiPayloadBuilder.payloadForCrmActionsSuggestions(escapedText); + + String response = openAiRequest.makeRequest(payload).getResponseBody(); + + return parseResponse(response); + } + + /** + * Parse the response from openai. + * @param responseBody + * @return + */ + private CrmActionSuggestionsFormatterDto parseResponse(String responseBody) { + CrmActionSuggestionsFormatterDto crmActionSuggestionsFormatterDto = new CrmActionSuggestionsFormatterDto(); + + try { + Util util = new Util(); + JsonNode rootNode = util.getJsonNode(responseBody); + JsonNode argumentsNode = rootNode.get("choices") + .get(0) + .get("message") + .get("function_call") + .get("arguments"); + + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); + String argumentsJson = objectMapper.convertValue(argumentsNode, String.class); + + Map> arguments = objectMapper.readValue(argumentsJson, + new TypeReference>>() { + }); + List addTaskList = arguments.get("add_task"); + + List formattedTaskSuggestionEntityDtos = new ArrayList<>(); + if (addTaskList != null) { + for (AddTaskSuggestionEntityDto addTask : addTaskList) { + AddTaskSuggestionEntityDto addTaskSuggestionEntityDto = new AddTaskSuggestionEntityDto(); + addTaskSuggestionEntityDto.setDescription(addTask.getDescription()); + + // Format the response check if duedate format is YYYY-MM-DD else + // remove duedate + String dueDate = addTask.getDueDate(); + if (dateFormatValidator.isValid(dueDate, null)) { + addTaskSuggestionEntityDto.setDueDate(dueDate); + } + + formattedTaskSuggestionEntityDtos.add(addTaskSuggestionEntityDto); + } + } + + crmActionSuggestionsFormatterDto.setAddTaskSuggestions(formattedTaskSuggestionEntityDtos); + return crmActionSuggestionsFormatterDto; + } + catch (Exception e) { + throw new CustomException(new ErrorObject("l_gcas_p_1", "something_went_wrong", e.getMessage())); + } + } + + /** + * Escape the input string for json. + * @param input + * @return + */ + private String escapeForJson(String input) { + return input.replace("\\", "\\\\") // Escape backslashes + .replace("\"", "\\\"") // Escape double quotes + .replace("\n", "\\n") // Escape newlines + .replace("\r", "\\r") // Escape carriage returns + .replace("\t", "\\t") // Escape tabs + .replace("\b", "\\b") // Escape backspace + .replace("\f", "\\f") // Escape form feeds + .replaceAll("[\u0000-\u001F]", ""); // Replace control characters with empty + // string + } + +} \ No newline at end of file diff --git a/src/main/java/com/salessparrow/api/lib/LocalCipher.java b/src/main/java/com/salessparrow/api/lib/LocalCipher.java index fcf6b94f..aeeaf1e5 100644 --- a/src/main/java/com/salessparrow/api/lib/LocalCipher.java +++ b/src/main/java/com/salessparrow/api/lib/LocalCipher.java @@ -20,100 +20,91 @@ @Component public class LocalCipher { - private final String algorithm = "AES"; - private final String encryptionMode = "AES/ECB/PKCS5Padding"; + private final String algorithm = "AES"; - /** - * Encrypt the input string. - * - * @param salt The salt for encryption. - * @param string The string to be encrypted. - * @return The encrypted string in hexadecimal format. - */ - public String encrypt(String salt, String string) { - byte[] encryptedBytes = null; - try { - Cipher encrypt = Cipher.getInstance(encryptionMode); - SecretKeySpec secretKey = generateSecretKey(salt); - encrypt.init(Cipher.ENCRYPT_MODE, secretKey); - encryptedBytes = encrypt.doFinal(string.getBytes(StandardCharsets.UTF_8)); - } catch (Exception e) { - throw new CustomException( - new ErrorObject( - "l_lc_e_1", - "something_went_wrong", - e.getMessage())); - } - return Base64.getEncoder().encodeToString(encryptedBytes); - } + private final String encryptionMode = "AES/ECB/PKCS5Padding"; - /** - * Decrypt the input encrypted string. - * - * @param salt The salt for decryption. - * @param encryptedString The encrypted string in hexadecimal format. - * @return The decrypted string. - */ - public String decrypt(String salt, String encryptedString) { - byte[] decryptedBytes = null; - try { - Cipher decrypt = Cipher.getInstance(encryptionMode); - SecretKeySpec secretKey = generateSecretKey(salt); - decrypt.init(Cipher.DECRYPT_MODE, secretKey); - byte[] decodedBytes = Base64.getDecoder().decode(encryptedString); - decryptedBytes = decrypt.doFinal(decodedBytes); - } catch (Exception e) { - throw new CustomException( - new ErrorObject( - "l_lc_d_1", - "something_went_wrong", - e.getMessage())); - } + /** + * Encrypt the input string. + * @param salt The salt for encryption. + * @param string The string to be encrypted. + * @return The encrypted string in hexadecimal format. + */ + public String encrypt(String salt, String string) { + byte[] encryptedBytes = null; + try { + Cipher encrypt = Cipher.getInstance(encryptionMode); + SecretKeySpec secretKey = generateSecretKey(salt); + encrypt.init(Cipher.ENCRYPT_MODE, secretKey); + encryptedBytes = encrypt.doFinal(string.getBytes(StandardCharsets.UTF_8)); + } + catch (Exception e) { + throw new CustomException(new ErrorObject("l_lc_e_1", "something_went_wrong", e.getMessage())); + } + return Base64.getEncoder().encodeToString(encryptedBytes); + } - return new String(decryptedBytes, StandardCharsets.UTF_8); - } + /** + * Decrypt the input encrypted string. + * @param salt The salt for decryption. + * @param encryptedString The encrypted string in hexadecimal format. + * @return The decrypted string. + */ + public String decrypt(String salt, String encryptedString) { + byte[] decryptedBytes = null; + try { + Cipher decrypt = Cipher.getInstance(encryptionMode); + SecretKeySpec secretKey = generateSecretKey(salt); + decrypt.init(Cipher.DECRYPT_MODE, secretKey); + byte[] decodedBytes = Base64.getDecoder().decode(encryptedString); + decryptedBytes = decrypt.doFinal(decodedBytes); + } + catch (Exception e) { + throw new CustomException(new ErrorObject("l_lc_d_1", "something_went_wrong", e.getMessage())); + } - /** - * Generate random IV. - * - * @param number The number of bytes for the IV. - * @return The random IV in hexadecimal format. - */ - public String generateRandomIv(int number) { - byte[] ivBytes = new byte[number]; - new SecureRandom().nextBytes(ivBytes); - return bytesToHex(ivBytes).substring(0, number * 2); - } + return new String(decryptedBytes, StandardCharsets.UTF_8); + } - /** - * Generate random salt. - * - * @return The random salt in hexadecimal format. - */ - public String generateRandomSalt() { - byte[] saltBytes = new byte[16]; - new SecureRandom().nextBytes(saltBytes); - return bytesToHex(saltBytes); - } + /** + * Generate random IV. + * @param number The number of bytes for the IV. + * @return The random IV in hexadecimal format. + */ + public String generateRandomIv(int number) { + byte[] ivBytes = new byte[number]; + new SecureRandom().nextBytes(ivBytes); + return bytesToHex(ivBytes).substring(0, number * 2); + } - private SecretKeySpec generateSecretKey(String salt) throws NoSuchAlgorithmException { - MessageDigest sha = MessageDigest.getInstance("SHA-256"); - byte[] keyBytes = sha.digest(salt.getBytes(StandardCharsets.UTF_8)); - byte[] key = new byte[16]; - System.arraycopy(keyBytes, 0, key, 0, 16); - return new SecretKeySpec(key, algorithm); - } + /** + * Generate random salt. + * @return The random salt in hexadecimal format. + */ + public String generateRandomSalt() { + byte[] saltBytes = new byte[16]; + new SecureRandom().nextBytes(saltBytes); + return bytesToHex(saltBytes); + } - private String bytesToHex(byte[] hash) { - StringBuilder hexString = new StringBuilder(2 * hash.length); - for (byte b : hash) { - String hex = Integer.toHexString(0xff & b); - if (hex.length() == 1) { - hexString.append('0'); - } - hexString.append(hex); - } - return hexString.toString(); - } -} + private SecretKeySpec generateSecretKey(String salt) throws NoSuchAlgorithmException { + MessageDigest sha = MessageDigest.getInstance("SHA-256"); + byte[] keyBytes = sha.digest(salt.getBytes(StandardCharsets.UTF_8)); + byte[] key = new byte[16]; + System.arraycopy(keyBytes, 0, key, 0, 16); + return new SecretKeySpec(key, algorithm); + } + + private String bytesToHex(byte[] hash) { + StringBuilder hexString = new StringBuilder(2 * hash.length); + for (byte b : hash) { + String hex = Integer.toHexString(0xff & b); + if (hex.length() == 1) { + hexString.append('0'); + } + hexString.append(hex); + } + return hexString.toString(); + } +} diff --git a/src/main/java/com/salessparrow/api/lib/UserLoginCookieAuth.java b/src/main/java/com/salessparrow/api/lib/UserLoginCookieAuth.java index 44314bdb..084785ae 100644 --- a/src/main/java/com/salessparrow/api/lib/UserLoginCookieAuth.java +++ b/src/main/java/com/salessparrow/api/lib/UserLoginCookieAuth.java @@ -20,176 +20,176 @@ @Component public class UserLoginCookieAuth { - Logger logger = LoggerFactory.getLogger(UserLoginCookieAuth.class); - - private String cookieValue; - private String userId; - private String userKind; - private Integer timestampInCookie; - private String token; - private User currentUser; - private String userLoginCookieValue; - private String decryptedEncryptionSalt; - - @Autowired - private LocalCipher localCipher; - - @Autowired - private CookieHelper cookieHelper; - - @Autowired - private SalesforceUserRepository salesforceUserRepository; - - /** - * Validate and set cookie - * - * @param cookieValue - * @param expireTimestamp - * - * @return Map - */ - public Map validateAndSetCookie(String cookieValue) { - this.cookieValue = cookieValue; - - logger.info("Validating cookie"); - validate(); - setParts(); - validateTimestamp(); - fetchAndValidateUser(); - validateCookieToken(); - setCookie(); - - Map resultMap = new HashMap<>(); - resultMap.put("current_user", currentUser); - resultMap.put("userLoginCookieValue", userLoginCookieValue); - - return resultMap; - } - - /** - * Validate cookie value and expire timestamp - * - * @throws RuntimeException - */ - private void validate() { - if (cookieValue == null || cookieValue.isEmpty()) { - - throw new CustomException( - new ErrorObject( - "l_ulca_v_1", - "unauthorized_api_request", - "Invalid cookie value")); - } - - } - - /** - * Set cookie value parts - * - * @throws RuntimeException - */ - private void setParts() { - List cookieValueParts = Arrays.asList(cookieValue.split(":")); - - if (cookieValueParts.size() != 5) { - throw new CustomException( - new ErrorObject( - "l_ulca_sp_1", - "unauthorized_api_request", - "Invalid cookie value")); - } - - if (cookieValueParts.get(0).equals(CookieConstants.LATEST_VERSION)) { - userId = cookieValueParts.get(1); - userKind = cookieValueParts.get(2); - timestampInCookie = Integer.parseInt(cookieValueParts.get(3)); - token = cookieValueParts.get(4); - } else { - throw new CustomException( - new ErrorObject( - "l_ulca_sp_2", - "unauthorized_api_request", - "Invalid cookie version")); - } - } - - /** - * Validate cookie timestamp - * - * @throws RuntimeException - */ - private void validateTimestamp() { - if (timestampInCookie + CookieConstants.USER_LOGIN_COOKIE_EXPIRY_IN_SEC < System.currentTimeMillis() / 1000) { - throw new CustomException( - new ErrorObject( - "l_ulca_vt_1", - "unauthorized_api_request", - "Cookie expired")); - - } - } - - /** - * Fetch and validate user - * - * @throws RuntimeException - */ - private void fetchAndValidateUser() { - - logger.info("Fetching and validating current user"); - if (userKind.equals(UserConstants.SALESFORCE_USER_KIND)) { - logger.info("Fetching and validating salesforce user"); - fetchAndValidateSalesforceUser(); - } else { - logger.info("Fetching another user"); - // Todo: Throw an error - } - - } - - private void fetchAndValidateSalesforceUser() { - User userObj = salesforceUserRepository.getSalesforceUserByExternalUserId(userId); - - if (userObj == null) { - throw new CustomException( - new ErrorObject( - "l_ulca_favu_1", - "unauthorized_api_request", - "User not found")); - } - - currentUser = userObj; - } - - /** - * Validate cookie token - * - * @throws RuntimeException - */ - private void validateCookieToken() { - String encryptionSalt = currentUser.getEncryptionSalt(); - decryptedEncryptionSalt = localCipher.decrypt(CoreConstants.encryptionKey(), - encryptionSalt); - - String generatedToken = cookieHelper.getCookieToken(currentUser, - decryptedEncryptionSalt, timestampInCookie); - if (!generatedToken.equals(token)) { - throw new CustomException( - new ErrorObject( - "l_ulca_vct_2", - "unauthorized_api_request", - "Invalid cookie token")); - } - } - - /** - * Set cookie - * - * @throws RuntimeException - */ - private void setCookie() { - userLoginCookieValue = cookieHelper.getCookieValue(currentUser, userKind, - decryptedEncryptionSalt); - } + + Logger logger = LoggerFactory.getLogger(UserLoginCookieAuth.class); + + private String cookieValue; + + private String reqApiSource; + + private String userId; + + private String userKind; + + private String cookieApiSource; + + private Integer timestampInCookie; + + private String token; + + private User currentUser; + + private String userLoginCookieValue; + + private String decryptedEncryptionSalt; + + @Autowired + private LocalCipher localCipher; + + @Autowired + private CookieHelper cookieHelper; + + @Autowired + private SalesforceUserRepository salesforceUserRepository; + + /** + * Validate and set cookie + * @param cookieValue + * @param reqApiSource + * @return Map + */ + public Map validateAndSetCookie(String cookieValue, String reqApiSource) { + this.cookieValue = cookieValue; + this.reqApiSource = reqApiSource; + + logger.info("Validating cookie"); + validate(); + setParts(); + validateTimestamp(); + validateApiSource(); + fetchAndValidateUser(); + validateCookieToken(); + setCookie(); + + Map resultMap = new HashMap<>(); + resultMap.put("current_user", currentUser); + resultMap.put("userLoginCookieValue", userLoginCookieValue); + + return resultMap; + } + + /** + * Validate cookie value and expire timestamp + * @throws RuntimeException + */ + private void validate() { + if (cookieValue == null || cookieValue.isEmpty()) { + + throw new CustomException( + new ErrorObject("l_ulca_v_1", "unauthorized_api_request", "Invalid cookie value")); + } + + } + + /** + * Set cookie value parts + * @throws RuntimeException + */ + private void setParts() { + List cookieValueParts = Arrays.asList(cookieValue.split(":")); + + if (cookieValueParts.size() != 6) { + throw new CustomException( + new ErrorObject("l_ulca_sp_1", "unauthorized_api_request", "Invalid cookie value")); + } + + if (cookieValueParts.get(0).equals(CookieConstants.LATEST_VERSION)) { + userId = cookieValueParts.get(1); + userKind = cookieValueParts.get(2); + cookieApiSource = cookieValueParts.get(3); + timestampInCookie = Integer.parseInt(cookieValueParts.get(4)); + token = cookieValueParts.get(5); + } + else { + throw new CustomException( + new ErrorObject("l_ulca_sp_2", "unauthorized_api_request", "Invalid cookie version")); + } + } + + /** + * Validate cookie timestamp + * @throws RuntimeException + */ + private void validateTimestamp() { + if (timestampInCookie + CookieConstants.USER_LOGIN_COOKIE_EXPIRY_IN_SEC < System.currentTimeMillis() / 1000) { + throw new CustomException(new ErrorObject("l_ulca_vt_1", "unauthorized_api_request", "Cookie expired")); + + } + } + + /** + * Validate api source + * @throws RuntimeException + */ + private void validateApiSource() { + if (!cookieApiSource.equals(reqApiSource)) { + throw new CustomException(new ErrorObject("l_ulca_vas_1", "unauthorized_api_request", + "Cookie api source and request api source mismatch")); + + } + } + + /** + * Fetch and validate user + * @throws RuntimeException + */ + private void fetchAndValidateUser() { + + logger.info("Fetching and validating current user"); + if (userKind.equals(UserConstants.SALESFORCE_USER_KIND)) { + logger.info("Fetching and validating salesforce user"); + fetchAndValidateSalesforceUser(); + } + else { + logger.info("Fetching another user"); + // Todo: Throw an error + } + + } + + private void fetchAndValidateSalesforceUser() { + User userObj = salesforceUserRepository.getSalesforceUserByExternalUserId(userId); + + if (userObj == null) { + throw new CustomException(new ErrorObject("l_ulca_favu_1", "unauthorized_api_request", "User not found")); + } + + currentUser = userObj; + } + + /** + * Validate cookie token + * @throws RuntimeException + */ + private void validateCookieToken() { + String encryptionSalt = currentUser.getEncryptionSalt(); + decryptedEncryptionSalt = localCipher.decrypt(CoreConstants.encryptionKey(), encryptionSalt); + + String generatedToken = cookieHelper.getCookieToken(currentUser, decryptedEncryptionSalt, timestampInCookie, + reqApiSource); + if (!generatedToken.equals(token)) { + throw new CustomException( + new ErrorObject("l_ulca_vct_2", "unauthorized_api_request", "Invalid cookie token")); + } + } + + /** + * Set cookie + * @throws RuntimeException + */ + private void setCookie() { + userLoginCookieValue = cookieHelper.getCookieValue(currentUser, userKind, decryptedEncryptionSalt, + reqApiSource); + } } diff --git a/src/main/java/com/salessparrow/api/lib/Util.java b/src/main/java/com/salessparrow/api/lib/Util.java index 65c341fe..2ca7e414 100644 --- a/src/main/java/com/salessparrow/api/lib/Util.java +++ b/src/main/java/com/salessparrow/api/lib/Util.java @@ -1,5 +1,12 @@ package com.salessparrow.api.lib; +import java.time.Instant; +import java.time.ZoneOffset; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.text.SimpleDateFormat; +import java.util.Date; + import org.springframework.stereotype.Component; import com.fasterxml.jackson.databind.JsonNode; @@ -11,54 +18,167 @@ /** * Class for utility functions. - * + * */ @Component public class Util { - /** - * Get JsonNode from json string - * - * @param jsonString - * - * @return JsonNode - */ - public JsonNode getJsonNode(String jsonString) { - ObjectMapper mapper = new ObjectMapper(); - JsonNode jsonNode = null; - try { - jsonNode = mapper.readTree(jsonString); - } catch (Exception e) { - throw new CustomException( - new ErrorObject( - "l_u_gjn_1", - "something_went_wrong", - e.getMessage())); - } - return jsonNode; - } + /** + * Get JsonNode from json string + * @param jsonString + * @return JsonNode + */ + public JsonNode getJsonNode(String jsonString) { + ObjectMapper mapper = new ObjectMapper(); + JsonNode jsonNode = null; + try { + jsonNode = mapper.readTree(jsonString); + } + catch (Exception e) { + throw new CustomException(new ErrorObject("l_u_gjn_1", "something_went_wrong", e.getMessage())); + } + return jsonNode; + } + + /** + * Retrieves a string representation of all request headers. For security, the value + * of the "authorization, cookie, password" header is obfuscated. + * @param request - The HTTP request containing the headers to be logged. + * @return String - A string representation of the headers in the format + * "{headerName:headerValue, ...}". + */ + public static String generateHeaderLogString(HttpServletRequest request) { + StringBuilder headerBuilder = new StringBuilder("{"); + request.getHeaderNames().asIterator().forEachRemaining(headerName -> { + // Add any other secret headers here that you don't want logged. + if (headerName.equals("authorization") || headerName.equals("cookie") || headerName.equals("password")) { + headerBuilder.append(headerName).append(":**********, "); + } + else { + headerBuilder.append(headerName).append(":").append(request.getHeader(headerName)).append(", "); + } + }); + headerBuilder.append("}"); + + return headerBuilder.toString(); + } + + /** + * Encode plain text to base64 + * @param plainText - String to be encoded + * @return String - Encoded string + */ + public static String base64Encode(String plainText) { + String encodedText = null; + + try { + encodedText = java.util.Base64.getEncoder().encodeToString(plainText.getBytes()); + } + catch (Exception e) { + throw new CustomException(new ErrorObject("l_u_b64e_1", "something_went_wrong", e.getMessage())); + } + + return encodedText; + } + + /** + * Decode base64 encoded text + * @param encodedText - String to be decoded + * @return String - Decoded string + */ + public static String base64Decode(String encodedText) { + String decodedText = null; + + try { + byte[] decodedBytes = java.util.Base64.getDecoder().decode(encodedText); + decodedText = new String(decodedBytes); + } + catch (Exception e) { + throw new CustomException(new ErrorObject("l_u_b64d_1", "something_went_wrong", e.getMessage())); + } + + return decodedText; + } + + /** + * Get current time in date format + * @return Date + */ + public static Date getCurrentTimeInDateFormat() { + Instant currentTimestamp = Instant.now(); + return Date.from(currentTimestamp.atOffset(ZoneOffset.UTC).toInstant()); + } + + /** + * Escape special characters in a string for use in a regular expression. SOQL is + * important from security point of view. Refer + * https://developer.salesforce.com/docs/atlas.en-us.soql_sosl.meta/soql_sosl/sforce_api_calls_soql_select_quotedstringescapes.htm + * for more details. + * @param input + * @return String + */ + public static String escapeSpecialChars(String input) { + if (input == null || input == "") + return input; + + String[] specialChars = { "\\", "%", "'", "\"", "_" }; + + for (String specialChar : specialChars) { + input = input.replace(specialChar, "\\" + specialChar); + } + + return input; + } + + /** + * URL encode a string + * @param input + * @return String + */ + public static String urlEncoder(String input) { + if (input == null || input == "") + return input; + + try { + return URLEncoder.encode(input, "UTF-8"); + } + catch (UnsupportedEncodingException e) { + throw new CustomException(new ErrorObject("u_u_ue_1", "something_went_wrong", e.getMessage())); + } + } + + /** + * Get date format from datetime + * @param date + * @return String + */ + public String getDateFormatFromDatetime(Date date) { + if (date != null) { + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); + return dateFormat.format(date); + } + return null; + } + + /** + * Unescape special characters in a string intended for plaintext contexts only. + * + * This function is intended for use only with strings that will be rendered as + * plaintext. It is NOT suitable for HTML strings, XML strings, or any other context + * where special characters may have syntactic meaning. + * + * Current Implementation: - The ampersand ("&") is unescaped to "&" + * + * Future versions may include additional un-escaping rules as needed. + * @param input The original string containing escaped special characters. + * @return A new string where certain special characters have been unescaped. + */ + public String unEscapeSpecialCharactersForPlainText(String input) { + return input.replace("&", "&"); + } + + public String replaceNewLineWithBreak(String input) { + return input.replace("\n", "
"); + } -/** - * Retrieves a string representation of all request headers. For security, - * the value of the "authorization, cookie, password" header is obfuscated. - * - * @param request - The HTTP request containing the headers to be logged. - * @return String - A string representation of the headers in the format "{headerName:headerValue, ...}". - */ - public static String generateHeaderLogString(HttpServletRequest request) { - StringBuilder headerBuilder = new StringBuilder("{"); - request.getHeaderNames().asIterator().forEachRemaining(headerName -> { - // Add any other secret headers here that you don't want logged. - if (headerName.equals("authorization") || - headerName.equals("cookie") || - headerName.equals("password")) { - headerBuilder.append(headerName).append(":**********, "); - } else { - headerBuilder.append(headerName).append(":").append(request.getHeader(headerName)).append(", "); - } - }); - headerBuilder.append("}"); - - return headerBuilder.toString(); - } } diff --git a/src/main/java/com/salessparrow/api/lib/crmActions/createNote/CreateNoteFactory.java b/src/main/java/com/salessparrow/api/lib/crmActions/createAccountNote/CreateNoteFactory.java similarity index 83% rename from src/main/java/com/salessparrow/api/lib/crmActions/createNote/CreateNoteFactory.java rename to src/main/java/com/salessparrow/api/lib/crmActions/createAccountNote/CreateNoteFactory.java index 99b4dfee..64eff701 100644 --- a/src/main/java/com/salessparrow/api/lib/crmActions/createNote/CreateNoteFactory.java +++ b/src/main/java/com/salessparrow/api/lib/crmActions/createAccountNote/CreateNoteFactory.java @@ -1,4 +1,4 @@ -package com.salessparrow.api.lib.crmActions.createNote; +package com.salessparrow.api.lib.crmActions.createAccountNote; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -15,6 +15,7 @@ */ @Component public class CreateNoteFactory { + @Autowired private CreateSalesforceNote createSalesforceNote; @@ -22,20 +23,17 @@ public class CreateNoteFactory { * Create a note for a given account * @param user * @param accountId - * * @return CreateNoteFormatterDto **/ public CreateNoteFormatterDto createNote(SalesforceUser user, String accountId, NoteDto note) { - switch(user.getUserKind()) { + switch (user.getUserKind()) { case UserConstants.SALESFORCE_USER_KIND: return createSalesforceNote.createNote(user, accountId, note); default: throw new CustomException( - new ErrorObject( - "l_ca_cn_cnf_cn_1", - "something_went_wrong", - "Invalid user kind.")); + new ErrorObject("l_ca_cn_cnf_cn_1", "something_went_wrong", "Invalid user kind.")); } } + } diff --git a/src/main/java/com/salessparrow/api/lib/crmActions/createNote/CreateNoteInterface.java b/src/main/java/com/salessparrow/api/lib/crmActions/createAccountNote/CreateNoteInterface.java similarity index 69% rename from src/main/java/com/salessparrow/api/lib/crmActions/createNote/CreateNoteInterface.java rename to src/main/java/com/salessparrow/api/lib/crmActions/createAccountNote/CreateNoteInterface.java index 0d3903db..614274ae 100644 --- a/src/main/java/com/salessparrow/api/lib/crmActions/createNote/CreateNoteInterface.java +++ b/src/main/java/com/salessparrow/api/lib/crmActions/createAccountNote/CreateNoteInterface.java @@ -1,4 +1,4 @@ -package com.salessparrow.api.lib.crmActions.createNote; +package com.salessparrow.api.lib.crmActions.createAccountNote; import org.springframework.stereotype.Component; @@ -11,6 +11,7 @@ */ @Component public interface CreateNoteInterface { - public CreateNoteFormatterDto createNote(SalesforceUser user, String accountId, NoteDto note); -} + public CreateNoteFormatterDto createNote(SalesforceUser user, String accountId, NoteDto note); + +} diff --git a/src/main/java/com/salessparrow/api/lib/crmActions/createAccountNote/CreateSalesforceNote.java b/src/main/java/com/salessparrow/api/lib/crmActions/createAccountNote/CreateSalesforceNote.java new file mode 100644 index 00000000..5e06f69e --- /dev/null +++ b/src/main/java/com/salessparrow/api/lib/crmActions/createAccountNote/CreateSalesforceNote.java @@ -0,0 +1,143 @@ +package com.salessparrow.api.lib.crmActions.createAccountNote; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.salessparrow.api.domain.SalesforceUser; +import com.salessparrow.api.dto.formatter.CreateNoteFormatterDto; +import com.salessparrow.api.exception.CustomException; +import com.salessparrow.api.dto.requestMapper.NoteDto; +import com.salessparrow.api.lib.Base64Helper; +import com.salessparrow.api.lib.Util; +import com.salessparrow.api.lib.errorLib.ErrorObject; +import com.salessparrow.api.lib.globalConstants.SalesforceConstants; +import com.salessparrow.api.lib.httpLib.HttpClient; +import com.salessparrow.api.lib.salesforce.dto.CompositeRequestDto; +import com.salessparrow.api.lib.salesforce.dto.SalesforceCreateNoteDto; +import com.salessparrow.api.lib.salesforce.helper.MakeCompositeRequest; + +/** + * CreateSalesforceNote is a class that creates a note in Salesforce and attaches it to an + * account. + */ +@Component +public class CreateSalesforceNote implements CreateNoteInterface { + + @Autowired + private SalesforceConstants salesforceConstants; + + @Autowired + private MakeCompositeRequest makeCompositeRequest; + + @Autowired + private Base64Helper base64Helper; + + Logger logger = LoggerFactory.getLogger(CreateSalesforceNote.class); + + /** + * Create a note for a given account. + * @param user + * @param accountId + * @param note + * @return CreateNoteFormatterDto + */ + public CreateNoteFormatterDto createNote(SalesforceUser user, String accountId, NoteDto note) { + String salesforceUserId = user.getExternalUserId(); + + Util util = new Util(); + String noteContent = note.getText(); + String noteTitle = getNoteTitleFromContent(noteContent); + noteContent = util.replaceNewLineWithBreak(noteContent); + String encodedNoteContent = base64Helper.base64Encode(noteContent); + + Map createNoteBody = new HashMap(); + createNoteBody.put("Title", noteTitle); + createNoteBody.put("Content", encodedNoteContent); + + CompositeRequestDto createNoteCompositeRequestDto = new CompositeRequestDto("POST", + salesforceConstants.salesforceCreateNoteUrl(), "CreateNote", createNoteBody); + + Map attachNoteBody = new HashMap(); + attachNoteBody.put("ContentDocumentId", "@{CreateNote.id}"); + attachNoteBody.put("LinkedEntityId", accountId); + + CompositeRequestDto attachNoteCompositeRequestDto = new CompositeRequestDto("POST", + salesforceConstants.salesforceAttachNoteUrl(), "AttachNote", attachNoteBody); + + List compositeRequests = new ArrayList(); + compositeRequests.add(createNoteCompositeRequestDto); + compositeRequests.add(attachNoteCompositeRequestDto); + + HttpClient.HttpResponse response = makeCompositeRequest.makePostRequest(compositeRequests, salesforceUserId); + + return parseResponse(response.getResponseBody()); + } + + /** + * Parse the response from Salesforce. + * @param createNoteFormatterDto + * @return CreateNoteFormatterDto - formatted response + */ + private CreateNoteFormatterDto parseResponse(String createNoteResponse) { + Util util = new Util(); + JsonNode rootNode = util.getJsonNode(createNoteResponse); + + JsonNode createNoteCompositeResponse = rootNode.get("compositeResponse").get(0); + String createNoteStatusCode = createNoteCompositeResponse.get("httpStatusCode").asText(); + + if (!createNoteStatusCode.equals("200") && !createNoteStatusCode.equals("201")) { + String errorBody = createNoteCompositeResponse.get("body").asText(); + + logger.error("createNoteCompositeResponse: " + createNoteCompositeResponse); + throw new CustomException(new ErrorObject("l_s_fse_fscn_fcn_1", "internal_server_error", errorBody)); + } + + JsonNode createNoteNodeResponseBody = rootNode.get("compositeResponse").get(0).get("body"); + + ObjectMapper mapper = new ObjectMapper(); + mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); + SalesforceCreateNoteDto salesforceCreateNoteDto = mapper.convertValue(createNoteNodeResponseBody, + SalesforceCreateNoteDto.class); + + JsonNode attachNoteCompositeResponse = rootNode.get("compositeResponse").get(1); + String attachNoteStatusCode = attachNoteCompositeResponse.get("httpStatusCode").asText(); + + if (!attachNoteStatusCode.equals("200") && !attachNoteStatusCode.equals("201")) { + String errorBody = attachNoteCompositeResponse.get("body").toString(); + + throw new CustomException(new ErrorObject("l_s_fse_fscn_fcn_2", "internal_server_error", errorBody)); + } + + CreateNoteFormatterDto createNoteFormatterDto = new CreateNoteFormatterDto(); + createNoteFormatterDto.setNoteId(salesforceCreateNoteDto.getId()); + + return createNoteFormatterDto; + } + + /** + * Get the first 50 characters of the note content. + * @param note - note dto + * @return String + */ + private String getNoteTitleFromContent(String noteContent) { + Util util = new Util(); + noteContent = util.unEscapeSpecialCharactersForPlainText(noteContent); + + if (noteContent.length() < 50) { + return noteContent; + } + + return noteContent.substring(0, 50); + } + +} diff --git a/src/main/java/com/salessparrow/api/lib/crmActions/createAccountTask/CreateAccountTask.java b/src/main/java/com/salessparrow/api/lib/crmActions/createAccountTask/CreateAccountTask.java new file mode 100644 index 00000000..eb57e094 --- /dev/null +++ b/src/main/java/com/salessparrow/api/lib/crmActions/createAccountTask/CreateAccountTask.java @@ -0,0 +1,17 @@ +package com.salessparrow.api.lib.crmActions.createAccountTask; + +import org.springframework.stereotype.Component; + +import com.salessparrow.api.domain.User; +import com.salessparrow.api.dto.formatter.CreateTaskFormatterDto; +import com.salessparrow.api.dto.requestMapper.CreateAccountTaskDto; + +/** + * CreateTask interface is interface for createTask for various CRM services + */ +@Component +public interface CreateAccountTask { + + public CreateTaskFormatterDto createAccountTask(User User, String accountId, CreateAccountTaskDto task); + +} diff --git a/src/main/java/com/salessparrow/api/lib/crmActions/createAccountTask/CreateAccountTaskFactory.java b/src/main/java/com/salessparrow/api/lib/crmActions/createAccountTask/CreateAccountTaskFactory.java new file mode 100644 index 00000000..bab2cac3 --- /dev/null +++ b/src/main/java/com/salessparrow/api/lib/crmActions/createAccountTask/CreateAccountTaskFactory.java @@ -0,0 +1,44 @@ +package com.salessparrow.api.lib.crmActions.createAccountTask; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.salessparrow.api.domain.User; +import com.salessparrow.api.dto.formatter.CreateTaskFormatterDto; +import com.salessparrow.api.dto.requestMapper.CreateAccountTaskDto; +import com.salessparrow.api.exception.CustomException; +import com.salessparrow.api.lib.errorLib.ErrorObject; +import com.salessparrow.api.lib.globalConstants.UserConstants; + +/** + * CreateTaskFactory class is responsible for creating a task in CRM + */ +@Component +public class CreateAccountTaskFactory { + + Logger logger = LoggerFactory.getLogger(CreateAccountTaskFactory.class); + + @Autowired + CreateSalesforceAccountTask createSalesforceAccountTask; + + /** + * Create a task in CRM + * @param user User object + * @param accountId CRM account id + * @param task CreateTaskDto object + * @return CreateTaskFormatterDto object + */ + public CreateTaskFormatterDto createAccountTask(User user, String accountId, CreateAccountTaskDto task) { + switch (user.getUserKind()) { + case UserConstants.SALESFORCE_USER_KIND: + logger.info("calling createTask of salesforceCreateTask"); + return createSalesforceAccountTask.createAccountTask(user, accountId, task); + default: + throw new CustomException( + new ErrorObject("l_ca_ct_ctf_1", "something_went_wrong", "Invalid user kind.")); + } + } + +} diff --git a/src/main/java/com/salessparrow/api/lib/crmActions/createAccountTask/CreateSalesforceAccountTask.java b/src/main/java/com/salessparrow/api/lib/crmActions/createAccountTask/CreateSalesforceAccountTask.java new file mode 100644 index 00000000..be02ebb9 --- /dev/null +++ b/src/main/java/com/salessparrow/api/lib/crmActions/createAccountTask/CreateSalesforceAccountTask.java @@ -0,0 +1,125 @@ +package com.salessparrow.api.lib.crmActions.createAccountTask; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.salessparrow.api.domain.User; +import com.salessparrow.api.dto.formatter.CreateTaskFormatterDto; +import com.salessparrow.api.dto.requestMapper.CreateAccountTaskDto; +import com.salessparrow.api.exception.CustomException; +import com.salessparrow.api.lib.Util; +import com.salessparrow.api.lib.errorLib.ErrorObject; +import com.salessparrow.api.lib.globalConstants.SalesforceConstants; +import com.salessparrow.api.lib.httpLib.HttpClient; +import com.salessparrow.api.lib.salesforce.dto.CompositeRequestDto; +import com.salessparrow.api.lib.salesforce.dto.SalesforceCreateTaskDto; +import com.salessparrow.api.lib.salesforce.helper.MakeCompositeRequest; + +/** + * CreateSalesforceTask class is responsible for creating a task in Salesforce + */ +@Component +public class CreateSalesforceAccountTask implements CreateAccountTask { + + Logger logger = LoggerFactory.getLogger(CreateSalesforceAccountTask.class); + + @Autowired + private SalesforceConstants salesforceConstants; + + @Autowired + private MakeCompositeRequest makeCompositeRequest; + + /** + * Create a task in Salesforce + * @param User User object + * @param accountId Salesforce account id + * @param task CreateTaskDto object + * @return CreateTaskFormatterDto object + */ + public CreateTaskFormatterDto createAccountTask(User User, String accountId, CreateAccountTaskDto task) { + String salesforceUserId = User.getExternalUserId(); + + logger.info("createAccountTask task description: {}", task.getDescription()); + Util util = new Util(); + String unEscapedTaskDescription = util.unEscapeSpecialCharactersForPlainText(task.getDescription()); + String taskSubject = getTaskSubjectFromDescription(unEscapedTaskDescription); + + logger.info("performing create task in salesforce"); + + Map taskBody = new HashMap(); + taskBody.put("Subject", taskSubject); + taskBody.put("Description", unEscapedTaskDescription); + taskBody.put("OwnerId", task.getCrmOrganizationUserId()); + taskBody.put("ActivityDate", task.getDueDate()); + taskBody.put("WhatId", accountId); + + CompositeRequestDto createTaskCompositeRequestDto = new CompositeRequestDto("POST", + salesforceConstants.salesforceCreateTaskUrl(), "CreateTask", taskBody); + + List compositeRequests = new ArrayList(); + compositeRequests.add(createTaskCompositeRequestDto); + + HttpClient.HttpResponse response = makeCompositeRequest.makePostRequest(compositeRequests, salesforceUserId); + + return parseResponse(response.getResponseBody()); + } + + /** + * Parse the response from Salesforce + * @param createTaskResponse String response from Salesforce + * @return CreateTaskFormatterDto object + */ + private CreateTaskFormatterDto parseResponse(String createTaskResponse) { + Util util = new Util(); + JsonNode rootNode = util.getJsonNode(createTaskResponse); + + logger.info("parsing response from salesforce"); + + JsonNode createTaskCompositeResponse = rootNode.get("compositeResponse").get(0); + String createTaskStatusCode = createTaskCompositeResponse.get("httpStatusCode").asText(); + + if (!createTaskStatusCode.equals("200") && !createTaskStatusCode.equals("201")) { + String errorBody = createTaskCompositeResponse.get("body").asText(); + + throw new CustomException(new ErrorObject("l_ca_ct_cst_1", "internal_server_error", errorBody)); + } + + JsonNode createTaskNodeResponseBody = rootNode.get("compositeResponse").get(0).get("body"); + + ObjectMapper mapper = new ObjectMapper(); + mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); + SalesforceCreateTaskDto salesforceCreateTaskDto = mapper.convertValue(createTaskNodeResponseBody, + SalesforceCreateTaskDto.class); + + CreateTaskFormatterDto createTaskFormatterDto = new CreateTaskFormatterDto(); + createTaskFormatterDto.setTaskId(salesforceCreateTaskDto.getId()); + + return createTaskFormatterDto; + } + + /** + * Get task subject from description + * @param task CreateTaskDto object + * @return String task subject + */ + private String getTaskSubjectFromDescription(String taskDescription) { + logger.info("getting task subject from description"); + + if (taskDescription.length() < 60) { + return taskDescription; + } + + return taskDescription.substring(0, 60); + } + +} diff --git a/src/main/java/com/salessparrow/api/lib/crmActions/createNote/CreateSalesforceNote.java b/src/main/java/com/salessparrow/api/lib/crmActions/createNote/CreateSalesforceNote.java deleted file mode 100644 index 0665a8ff..00000000 --- a/src/main/java/com/salessparrow/api/lib/crmActions/createNote/CreateSalesforceNote.java +++ /dev/null @@ -1,148 +0,0 @@ -package com.salessparrow.api.lib.crmActions.createNote; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.salessparrow.api.domain.SalesforceUser; -import com.salessparrow.api.dto.formatter.CreateNoteFormatterDto; -import com.salessparrow.api.exception.CustomException; -import com.salessparrow.api.dto.requestMapper.NoteDto; -import com.salessparrow.api.lib.Base64Helper; -import com.salessparrow.api.lib.Util; -import com.salessparrow.api.lib.errorLib.ErrorObject; -import com.salessparrow.api.lib.globalConstants.SalesforceConstants; -import com.salessparrow.api.lib.httpLib.HttpClient; -import com.salessparrow.api.lib.salesforce.dto.CompositeRequestDto; -import com.salessparrow.api.lib.salesforce.dto.SalesforceCreateNoteDto; -import com.salessparrow.api.lib.salesforce.helper.MakeCompositeRequest; - -/** - * CreateSalesforceNote is a class that creates a note in Salesforce and attaches it to an account. - */ -@Component -public class CreateSalesforceNote implements CreateNoteInterface { - - @Autowired - private SalesforceConstants salesforceConstants; - - @Autowired - private MakeCompositeRequest makeCompositeRequest; - - @Autowired - private Base64Helper base64Helper; - - /** - * Create a note for a given account. - * - * @param user - * @param accountId - * @param note - * @return CreateNoteFormatterDto - */ - public CreateNoteFormatterDto createNote(SalesforceUser user, String accountId, NoteDto note) { - String salesforceUserId = user.getExternalUserId(); - - String noteTitle = getNoteTitleFromContent(note); - String encodedNoteContent = base64Helper.base64Encode(note.getText()); - - Map createNoteBody = new HashMap(); - createNoteBody.put("Title", noteTitle); - createNoteBody.put("Content", encodedNoteContent); - - CompositeRequestDto createNoteCompositeRequestDto = new CompositeRequestDto( - "POST", - salesforceConstants.salesforceCreateNoteUrl(), - "CreateNote", - createNoteBody - ); - - Map attachNoteBody = new HashMap(); - attachNoteBody.put("ContentDocumentId", "@{CreateNote.id}"); - attachNoteBody.put("LinkedEntityId", accountId); - - CompositeRequestDto attachNoteCompositeRequestDto = new CompositeRequestDto( - "POST", - salesforceConstants.salesforceAttachNoteUrl(), - "AttachNote", - attachNoteBody - ); - - List compositeRequests = new ArrayList(); - compositeRequests.add(createNoteCompositeRequestDto); - compositeRequests.add(attachNoteCompositeRequestDto); - - HttpClient.HttpResponse response = makeCompositeRequest.makePostRequest(compositeRequests, salesforceUserId); - - return parseResponse(response.getResponseBody()); - } - - /** - * Parse the response from Salesforce. - * - * @param createNoteFormatterDto - * @return CreateNoteFormatterDto - formatted response - */ - private CreateNoteFormatterDto parseResponse(String createNoteResponse) { - Util util = new Util(); - JsonNode rootNode = util.getJsonNode(createNoteResponse); - - JsonNode createNoteCompositeResponse = rootNode.get("compositeResponse").get(0); - String createNoteStatusCode = createNoteCompositeResponse.get("httpStatusCode").asText(); - - if (!createNoteStatusCode.equals("200") && !createNoteStatusCode.equals("201")) { - String errorBody = createNoteCompositeResponse.get("body").asText(); - - throw new CustomException( - new ErrorObject( - "l_s_fse_fscn_fcn_1", - "internal_server_error", - errorBody)); - } - - JsonNode createNoteNodeResponseBody = rootNode.get("compositeResponse").get(0).get("body"); - - ObjectMapper mapper = new ObjectMapper(); - mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); - SalesforceCreateNoteDto salesforceCreateNoteDto = mapper.convertValue(createNoteNodeResponseBody, SalesforceCreateNoteDto.class); - - JsonNode attachNoteCompositeResponse = rootNode.get("compositeResponse").get(1); - String attachNoteStatusCode = attachNoteCompositeResponse.get("httpStatusCode").asText(); - - if (!attachNoteStatusCode.equals("200") && !attachNoteStatusCode.equals("201")) { - String errorBody = attachNoteCompositeResponse.get("body").toString(); - - throw new CustomException( - new ErrorObject( - "l_s_fse_fscn_fcn_2", - "internal_server_error", - errorBody)); - } - - CreateNoteFormatterDto createNoteFormatterDto = new CreateNoteFormatterDto(); - createNoteFormatterDto.setNoteId(salesforceCreateNoteDto.getId()); - - return createNoteFormatterDto; - } - - /** - * Get the first 50 characters of the note content. - * - * @param note - note dto - * @return String - */ - private String getNoteTitleFromContent(NoteDto note) { - if (note.getText().length() < 50) { - return note.getText(); - } - - return note.getText().substring(0, 50); - } -} diff --git a/src/main/java/com/salessparrow/api/lib/crmActions/deleteAccountNote/DeleteAccountNoteFactory.java b/src/main/java/com/salessparrow/api/lib/crmActions/deleteAccountNote/DeleteAccountNoteFactory.java new file mode 100644 index 00000000..c3cfce49 --- /dev/null +++ b/src/main/java/com/salessparrow/api/lib/crmActions/deleteAccountNote/DeleteAccountNoteFactory.java @@ -0,0 +1,46 @@ +package com.salessparrow.api.lib.crmActions.deleteAccountNote; + +import org.slf4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.salessparrow.api.domain.User; +import com.salessparrow.api.exception.CustomException; +import com.salessparrow.api.lib.errorLib.ErrorObject; +import com.salessparrow.api.lib.globalConstants.UserConstants; + +/** + * DeleteAccountNoteFactory is a factory class for the DeleteAccountNote action for the + * CRM. + */ +@Component +public class DeleteAccountNoteFactory { + + private Logger logger = org.slf4j.LoggerFactory.getLogger(DeleteAccountNoteFactory.class); + + @Autowired + DeleteSalesforceAccountNote getSalesforceNoteDetails; + + @Autowired + DeleteSalesforceAccountNote deleteAccountSalesforceNote; + + /** + * deleteAccountNote is a method that makes call to delete note based on user kind. + * @param user + * @param noteId + * @return void + */ + public void deleteAccountNote(User user, String noteId) { + logger.info("Delete Account Note Factory called"); + + switch (user.getUserKind()) { + case UserConstants.SALESFORCE_USER_KIND: + deleteAccountSalesforceNote.deleteAccountNote(user, noteId); + break; + default: + throw new CustomException( + new ErrorObject("l_ca_dan_danf_dn_1", "something_went_wrong", "Invalid user kind.")); + } + } + +} diff --git a/src/main/java/com/salessparrow/api/lib/crmActions/deleteAccountNote/DeleteAccountNoteInterface.java b/src/main/java/com/salessparrow/api/lib/crmActions/deleteAccountNote/DeleteAccountNoteInterface.java new file mode 100644 index 00000000..83a6f669 --- /dev/null +++ b/src/main/java/com/salessparrow/api/lib/crmActions/deleteAccountNote/DeleteAccountNoteInterface.java @@ -0,0 +1,16 @@ +package com.salessparrow.api.lib.crmActions.deleteAccountNote; + +import org.springframework.stereotype.Component; + +import com.salessparrow.api.domain.User; + +/** + * DeleteAccountNoteInterface is an interface for the DeleteAccountNote action for the + * CRM. + */ +@Component +public interface DeleteAccountNoteInterface { + + public void deleteAccountNote(User user, String noteId); + +} diff --git a/src/main/java/com/salessparrow/api/lib/crmActions/deleteAccountNote/DeleteSalesforceAccountNote.java b/src/main/java/com/salessparrow/api/lib/crmActions/deleteAccountNote/DeleteSalesforceAccountNote.java new file mode 100644 index 00000000..07bd0299 --- /dev/null +++ b/src/main/java/com/salessparrow/api/lib/crmActions/deleteAccountNote/DeleteSalesforceAccountNote.java @@ -0,0 +1,88 @@ +package com.salessparrow.api.lib.crmActions.deleteAccountNote; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.slf4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.fasterxml.jackson.databind.JsonNode; +import com.salessparrow.api.domain.User; +import com.salessparrow.api.exception.CustomException; +import com.salessparrow.api.lib.Util; +import com.salessparrow.api.lib.errorLib.ErrorObject; +import com.salessparrow.api.lib.errorLib.ParamErrorObject; +import com.salessparrow.api.lib.globalConstants.SalesforceConstants; +import com.salessparrow.api.lib.httpLib.HttpClient; +import com.salessparrow.api.lib.salesforce.dto.CompositeRequestDto; +import com.salessparrow.api.lib.salesforce.helper.MakeCompositeRequest; + +/** + * DeleteAccountSalesforceNote is a class for the DeleteAccountNote service for the + * Salesforce CRM. + **/ +@Component +public class DeleteSalesforceAccountNote implements DeleteAccountNoteInterface { + + private Logger logger = org.slf4j.LoggerFactory.getLogger(DeleteSalesforceAccountNote.class); + + @Autowired + private SalesforceConstants salesforceConstants; + + @Autowired + private MakeCompositeRequest makeCompositeRequest; + + /** + * Deletes a note from salesforce + * @param user + * @param noteId + * @return void + **/ + public void deleteAccountNote(User user, String noteId) { + logger.info("Delete Salesforce Account Note called"); + + String salesforceUserId = user.getExternalUserId(); + + String url = salesforceConstants.salesforceDeleteNoteUrl(noteId); + + CompositeRequestDto compositeReq = new CompositeRequestDto("DELETE", url, "DeleteNote"); + + List compositeRequests = new ArrayList(); + compositeRequests.add(compositeReq); + + HttpClient.HttpResponse response = makeCompositeRequest.makePostRequest(compositeRequests, salesforceUserId); + + parseResponse(response.getResponseBody()); + } + + /** + * Parse Response + * @param responseBody + * @return void + **/ + public void parseResponse(String responseBody) { + + Util util = new Util(); + JsonNode rootNode = util.getJsonNode(responseBody); + + JsonNode deleteNoteCompositeResponse = rootNode.get("compositeResponse").get(0); + Integer deleteNoteStatusCode = deleteNoteCompositeResponse.get("httpStatusCode").asInt(); + + if (deleteNoteStatusCode != 200 && deleteNoteStatusCode != 201 && deleteNoteStatusCode != 204) { + logger.error("Error in deleting note in salesforce:{}", deleteNoteCompositeResponse); + String errorBody = deleteNoteCompositeResponse.get("body").asText(); + + // MALFORMED_ID or NOT_FOUND + if (deleteNoteStatusCode == 400 || deleteNoteStatusCode == 404) { + throw new CustomException( + new ParamErrorObject("l_ca_dan_dasn_pr_1", errorBody, Arrays.asList("invalid_note_id"))); + } + else { + throw new CustomException(new ErrorObject("l_ca_dan_dasn_pr_2", "something_went_wrong", errorBody)); + } + } + } + +} \ No newline at end of file diff --git a/src/main/java/com/salessparrow/api/lib/crmActions/deleteAccountTask/DeleteAccountTask.java b/src/main/java/com/salessparrow/api/lib/crmActions/deleteAccountTask/DeleteAccountTask.java new file mode 100644 index 00000000..2c225e68 --- /dev/null +++ b/src/main/java/com/salessparrow/api/lib/crmActions/deleteAccountTask/DeleteAccountTask.java @@ -0,0 +1,15 @@ +package com.salessparrow.api.lib.crmActions.deleteAccountTask; + +import org.springframework.stereotype.Component; + +import com.salessparrow.api.domain.User; + +/** + * DeleteAccountTask is an interface for deleting a task in an account. + */ +@Component +public interface DeleteAccountTask { + + public void deleteAccountTask(User user, String accountId, String taskId); + +} diff --git a/src/main/java/com/salessparrow/api/lib/crmActions/deleteAccountTask/DeleteAccountTaskFactory.java b/src/main/java/com/salessparrow/api/lib/crmActions/deleteAccountTask/DeleteAccountTaskFactory.java new file mode 100644 index 00000000..7da8282e --- /dev/null +++ b/src/main/java/com/salessparrow/api/lib/crmActions/deleteAccountTask/DeleteAccountTaskFactory.java @@ -0,0 +1,44 @@ +package com.salessparrow.api.lib.crmActions.deleteAccountTask; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.salessparrow.api.domain.User; +import com.salessparrow.api.exception.CustomException; +import com.salessparrow.api.lib.errorLib.ErrorObject; +import com.salessparrow.api.lib.globalConstants.UserConstants; + +/** + * DeleteAccountTaskFactory is a factory class that handles the deleting a task in an + * account. + */ +@Component +public class DeleteAccountTaskFactory { + + Logger logger = LoggerFactory.getLogger(DeleteAccountTaskFactory.class); + + @Autowired + DeleteSalesforceAccountTask deleteSalesforceAccountTask; + + /** + * Deletes a task in an account. + * @param user + * @param accountId + * @param taskId + * @return void + */ + public void deleteAccountTask(User user, String accountId, String taskId) { + logger.info("Delete Account Task Factory called"); + switch (user.getUserKind()) { + case UserConstants.SALESFORCE_USER_KIND: + deleteSalesforceAccountTask.deleteAccountTask(user, accountId, taskId); + break; + default: + throw new CustomException( + new ErrorObject("l_ca_dat_datf_1", "something_went_wrong", "Invalid user kind.")); + } + } + +} diff --git a/src/main/java/com/salessparrow/api/lib/crmActions/deleteAccountTask/DeleteSalesforceAccountTask.java b/src/main/java/com/salessparrow/api/lib/crmActions/deleteAccountTask/DeleteSalesforceAccountTask.java new file mode 100644 index 00000000..ea237034 --- /dev/null +++ b/src/main/java/com/salessparrow/api/lib/crmActions/deleteAccountTask/DeleteSalesforceAccountTask.java @@ -0,0 +1,92 @@ +package com.salessparrow.api.lib.crmActions.deleteAccountTask; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.fasterxml.jackson.databind.JsonNode; +import com.salessparrow.api.domain.User; +import com.salessparrow.api.exception.CustomException; +import com.salessparrow.api.lib.Util; +import com.salessparrow.api.lib.errorLib.ErrorObject; +import com.salessparrow.api.lib.errorLib.ParamErrorObject; +import com.salessparrow.api.lib.globalConstants.SalesforceConstants; +import com.salessparrow.api.lib.httpLib.HttpClient; +import com.salessparrow.api.lib.salesforce.dto.CompositeRequestDto; +import com.salessparrow.api.lib.salesforce.helper.MakeCompositeRequest; + +/** + * DeleteSalesforceAccountTask is a class that handles the deleting a task in an account + * for salesforce. + */ +@Component +public class DeleteSalesforceAccountTask implements DeleteAccountTask { + + Logger logger = LoggerFactory.getLogger(DeleteSalesforceAccountTask.class); + + @Autowired + private SalesforceConstants salesforceConstants; + + @Autowired + private MakeCompositeRequest makeCompositeRequest; + + /** + * Deletes a task in an account for salesforce. + * @param user + * @param accountId + * @param taskId + * @return void + */ + public void deleteAccountTask(User user, String accountId, String taskId) { + logger.info("Delete Salesforce Account Task called"); + + String salesforceUserId = user.getExternalUserId(); + + String url = salesforceConstants.salesforceDeleteAccountTaskUrl(taskId); + + CompositeRequestDto compositeReq = new CompositeRequestDto("DELETE", url, "DeleteTask"); + + List compositeRequests = new ArrayList(); + compositeRequests.add(compositeReq); + + HttpClient.HttpResponse response = makeCompositeRequest.makePostRequest(compositeRequests, salesforceUserId); + + parseResponse(response.getResponseBody()); + + } + + /** + * Parses the response from salesforce. + * @param responseBody + * @return void + */ + private void parseResponse(String responseBody) { + logger.info("Parsing response body"); + Util util = new Util(); + JsonNode rootNode = util.getJsonNode(responseBody); + + JsonNode deleteNoteCompositeResponse = rootNode.get("compositeResponse").get(0); + Integer deleteNoteStatusCode = deleteNoteCompositeResponse.get("httpStatusCode").asInt(); + + if (deleteNoteStatusCode != 200 && deleteNoteStatusCode != 201 && deleteNoteStatusCode != 204) { + logger.error("Error in deleting task in salesforce:{}", deleteNoteCompositeResponse); + String errorBody = deleteNoteCompositeResponse.get("body").asText(); + + // MALFORMED_ID or NOT_FOUND + if (deleteNoteStatusCode == 400 || deleteNoteStatusCode == 404) { + + throw new CustomException( + new ParamErrorObject("l_ca_dan_dasn_pr_1", errorBody, Arrays.asList("invalid_task_id"))); + } + else { + throw new CustomException(new ErrorObject("l_ca_dan_dasn_pr_2", "something_went_wrong", errorBody)); + } + } + } + +} diff --git a/src/main/java/com/salessparrow/api/lib/crmActions/disconnectUser/DisconnectSalesforceUser.java b/src/main/java/com/salessparrow/api/lib/crmActions/disconnectUser/DisconnectSalesforceUser.java new file mode 100644 index 00000000..7f84bee5 --- /dev/null +++ b/src/main/java/com/salessparrow/api/lib/crmActions/disconnectUser/DisconnectSalesforceUser.java @@ -0,0 +1,68 @@ +package com.salessparrow.api.lib.crmActions.disconnectUser; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.salessparrow.api.domain.SalesforceOauthToken; +import com.salessparrow.api.domain.User; +import com.salessparrow.api.exception.CustomException; +import com.salessparrow.api.lib.AwsKms; +import com.salessparrow.api.lib.errorLib.ErrorObject; +import com.salessparrow.api.lib.salesforce.wrappers.SalesforceTokens; +import com.salessparrow.api.repositories.SalesforceOauthTokenRepository; +import com.salessparrow.api.repositories.SalesforceUserRepository; + +/** + * DisconnectSalesforceUser class to disconnect a user from Salesforce. + */ +@Component +public class DisconnectSalesforceUser implements DisconnectUser { + + @Autowired + private SalesforceOauthTokenRepository salesforceOauthTokenRepository; + + @Autowired + private SalesforceUserRepository salesforceUserRepository; + + @Autowired + private AwsKms awsKms; + + @Autowired + private SalesforceTokens salesforceTokens; + + private static final Logger logger = LoggerFactory.getLogger(DisconnectSalesforceUser.class); + + /** + * Disconnects a user from Salesforce by revoking the tokens and deleting the user + * data from the database. + * @param user + * @return void + */ + public void disconnect(User user) { + + String salesforceUserId = user.getExternalUserId(); + + logger.info("Disconnecting user from Salesforce: " + salesforceUserId); + SalesforceOauthToken salesforceOauthToken = salesforceOauthTokenRepository + .getSalesforceOauthTokenByExternalUserId(salesforceUserId); + + if (salesforceOauthToken == null) { + throw new CustomException( + new ErrorObject("l_ca_du_dsu_d_1", "something_went_wrong", "No tokens data found for this user.")); + } + + String decryptedRefreshToken = awsKms.decryptToken(salesforceOauthToken.getRefreshToken()); + + logger.info("Revoking tokens from Salesforce: " + salesforceUserId); + salesforceTokens.revokeTokens(salesforceOauthToken.getInstanceUrl(), decryptedRefreshToken); + + logger.info("Deleting tokens from database: " + salesforceUserId); + salesforceOauthTokenRepository.deleteSalesforceOauthTokenBySalesforceOauthToken(salesforceOauthToken); + + logger.info("Deleting user data from database: " + salesforceUserId); + salesforceUserRepository.removeSalesforceUserData(salesforceUserId); + } + +} diff --git a/src/main/java/com/salessparrow/api/lib/crmActions/disconnectUser/DisconnectUser.java b/src/main/java/com/salessparrow/api/lib/crmActions/disconnectUser/DisconnectUser.java new file mode 100644 index 00000000..e0d9e143 --- /dev/null +++ b/src/main/java/com/salessparrow/api/lib/crmActions/disconnectUser/DisconnectUser.java @@ -0,0 +1,15 @@ +package com.salessparrow.api.lib.crmActions.disconnectUser; + +import org.springframework.stereotype.Component; + +import com.salessparrow.api.domain.User; + +/** + * DisconnectUser interface for disconnecting a user from the CRM. + */ +@Component +public interface DisconnectUser { + + public void disconnect(User User); + +} diff --git a/src/main/java/com/salessparrow/api/lib/crmActions/disconnectUser/DisconnectUserFactory.java b/src/main/java/com/salessparrow/api/lib/crmActions/disconnectUser/DisconnectUserFactory.java new file mode 100644 index 00000000..783200d5 --- /dev/null +++ b/src/main/java/com/salessparrow/api/lib/crmActions/disconnectUser/DisconnectUserFactory.java @@ -0,0 +1,37 @@ +package com.salessparrow.api.lib.crmActions.disconnectUser; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.salessparrow.api.domain.User; +import com.salessparrow.api.exception.CustomException; +import com.salessparrow.api.lib.errorLib.ErrorObject; +import com.salessparrow.api.lib.globalConstants.UserConstants; + +/** + * Factory for disconnecting a user from the CRM based on the user kind. + */ +@Component +public class DisconnectUserFactory { + + @Autowired + private DisconnectSalesforceUser disconnectSalesforceUser; + + /** + * Disconnect a user from the CRM based on the user kind. + * @param user + * @return void + */ + public void disconnect(User user) { + + switch (user.getUserKind()) { + case UserConstants.SALESFORCE_USER_KIND: + disconnectSalesforceUser.disconnect(user); + break; + default: + throw new CustomException( + new ErrorObject("l_ca_du_duf_d_1", "something_went_wrong", "Invalid user kind.")); + } + } + +} diff --git a/src/main/java/com/salessparrow/api/lib/crmActions/getNoteDetails/GetNoteDetails.java b/src/main/java/com/salessparrow/api/lib/crmActions/getAccountNoteDetails/GetAccountNoteDetails.java similarity index 58% rename from src/main/java/com/salessparrow/api/lib/crmActions/getNoteDetails/GetNoteDetails.java rename to src/main/java/com/salessparrow/api/lib/crmActions/getAccountNoteDetails/GetAccountNoteDetails.java index f774626c..ea06b893 100644 --- a/src/main/java/com/salessparrow/api/lib/crmActions/getNoteDetails/GetNoteDetails.java +++ b/src/main/java/com/salessparrow/api/lib/crmActions/getAccountNoteDetails/GetAccountNoteDetails.java @@ -1,4 +1,4 @@ -package com.salessparrow.api.lib.crmActions.getNoteDetails; +package com.salessparrow.api.lib.crmActions.getAccountNoteDetails; import org.springframework.stereotype.Component; @@ -9,6 +9,8 @@ * GetNoteDetails is an interface for the GetNoteDetails action for the CRM. */ @Component -public interface GetNoteDetails { - public GetNoteDetailsFormatterDto getNoteDetails(User user, String noteId); +public interface GetAccountNoteDetails { + + public GetNoteDetailsFormatterDto getNoteDetails(User user, String noteId); + } diff --git a/src/main/java/com/salessparrow/api/lib/crmActions/getAccountNoteDetails/GetAccountNoteDetailsFactory.java b/src/main/java/com/salessparrow/api/lib/crmActions/getAccountNoteDetails/GetAccountNoteDetailsFactory.java new file mode 100644 index 00000000..cee21300 --- /dev/null +++ b/src/main/java/com/salessparrow/api/lib/crmActions/getAccountNoteDetails/GetAccountNoteDetailsFactory.java @@ -0,0 +1,38 @@ +package com.salessparrow.api.lib.crmActions.getAccountNoteDetails; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.salessparrow.api.domain.User; +import com.salessparrow.api.dto.formatter.GetNoteDetailsFormatterDto; +import com.salessparrow.api.exception.CustomException; +import com.salessparrow.api.lib.errorLib.ErrorObject; +import com.salessparrow.api.lib.globalConstants.UserConstants; + +/** + * GetNoteDetailsFactory is a factory class for the GetNoteDetails action for the CRM. + */ +@Component +public class GetAccountNoteDetailsFactory { + + @Autowired + GetSalesforceAccountNoteDetails getSalesforceNoteDetails; + + /** + * getNoteDetails is a method that returns the details of a note. + * @param user + * @param noteId + * @return GetNoteDetailsFormatterDto + */ + public GetNoteDetailsFormatterDto getNoteDetails(User user, String noteId) { + + switch (user.getUserKind()) { + case UserConstants.SALESFORCE_USER_KIND: + return getSalesforceNoteDetails.getNoteDetails(user, noteId); + default: + throw new CustomException( + new ErrorObject("l_ca_gnd_gndf_gnd_1", "something_went_wrong", "Invalid user kind.")); + } + } + +} diff --git a/src/main/java/com/salessparrow/api/lib/crmActions/getAccountNoteDetails/GetSalesforceAccountNoteDetails.java b/src/main/java/com/salessparrow/api/lib/crmActions/getAccountNoteDetails/GetSalesforceAccountNoteDetails.java new file mode 100644 index 00000000..94fec882 --- /dev/null +++ b/src/main/java/com/salessparrow/api/lib/crmActions/getAccountNoteDetails/GetSalesforceAccountNoteDetails.java @@ -0,0 +1,115 @@ +package com.salessparrow.api.lib.crmActions.getAccountNoteDetails; + +import java.util.ArrayList; +import java.util.List; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.salessparrow.api.domain.User; +import com.salessparrow.api.dto.entities.NoteDetailEntity; +import com.salessparrow.api.dto.formatter.GetNoteDetailsFormatterDto; +import com.salessparrow.api.exception.CustomException; +import com.salessparrow.api.lib.Util; +import com.salessparrow.api.lib.errorLib.ErrorObject; +import com.salessparrow.api.lib.globalConstants.SalesforceConstants; +import com.salessparrow.api.lib.httpLib.HttpClient; +import com.salessparrow.api.lib.salesforce.dto.CompositeRequestDto; +import com.salessparrow.api.lib.salesforce.helper.MakeCompositeRequest; +import com.salessparrow.api.lib.salesforce.dto.SalesforceGetNoteDetailsDto; +import com.salessparrow.api.lib.salesforce.wrappers.SalesforceGetNoteContent; +import com.salessparrow.api.lib.salesforce.helper.SalesforceQueryBuilder; + +@Component +public class GetSalesforceAccountNoteDetails implements GetAccountNoteDetails { + + @Autowired + private SalesforceConstants salesforceConstants; + + @Autowired + private MakeCompositeRequest makeCompositeRequest; + + @Autowired + private SalesforceGetNoteContent salesforceGetNoteContent; + + /** + * Get the details of a note + * @param user + * @param noteId + * @return GetNoteDetailsFormatterDto + **/ + public GetNoteDetailsFormatterDto getNoteDetails(User user, String noteId) { + + String salesforceUserId = user.getExternalUserId(); + + HttpClient.HttpResponse noteDetailsResponse = notesResponse(noteId, salesforceUserId); + + HttpClient.HttpResponse noteContentResponse = salesforceGetNoteContent.getNoteContent(noteId, salesforceUserId); + + GetNoteDetailsFormatterDto noteDetailsFormatterDto = parseResponse(noteDetailsResponse.getResponseBody(), + noteContentResponse.getResponseBody()); + + return noteDetailsFormatterDto; + + } + + /** + * Get the list of notes for a given account + * @param documentIds + * @param salesforceUserId + * @return HttpResponse + **/ + private HttpClient.HttpResponse notesResponse(String noteId, String salesforceUserId) { + SalesforceQueryBuilder salesforceLib = new SalesforceQueryBuilder(); + String notesQuery = salesforceLib.getNoteDetailsUrl(noteId); + + String notesUrl = salesforceConstants.queryUrlPath() + notesQuery; + + CompositeRequestDto noteCompositeRequest = new CompositeRequestDto("GET", notesUrl, "GetNotesList"); + + List compositeRequests = new ArrayList(); + compositeRequests.add(noteCompositeRequest); + + HttpClient.HttpResponse response = makeCompositeRequest.makePostRequest(compositeRequests, salesforceUserId); + + return response; + } + + /** + * Format the response of the note details + * @param noteDetailsResponse + * @param noteContentResponse + * @return GetNoteDetailsFormatterDto + */ + private GetNoteDetailsFormatterDto parseResponse(String noteDetailsResponse, String noteContentResponse) { + NoteDetailEntity noteDetailEntity = new NoteDetailEntity(); + try { + Util util = new Util(); + JsonNode rootNode = util.getJsonNode(noteDetailsResponse); + JsonNode recordsNode = rootNode.get("compositeResponse").get(0).get("body").get("records"); + + for (JsonNode recordNode : recordsNode) { + ObjectMapper mapper = new ObjectMapper(); + mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); + SalesforceGetNoteDetailsDto salesforceGetNotesList = mapper.convertValue(recordNode, + SalesforceGetNoteDetailsDto.class); + noteDetailEntity = salesforceGetNotesList.noteDetailEntity(noteContentResponse); + } + if (recordsNode.size() == 0) { + throw new CustomException(new ErrorObject("l_c_gnd_gsnd_1", "something_went_wrong", "Note not found")); + } + } + catch (Exception e) { + throw new CustomException(new ErrorObject("l_c_gnd_gsnd_2", "something_went_wrong", e.getMessage())); + } + + GetNoteDetailsFormatterDto getNoteDetailsFormatterDto = new GetNoteDetailsFormatterDto(noteDetailEntity); + + return getNoteDetailsFormatterDto; + + } + +} diff --git a/src/main/java/com/salessparrow/api/lib/crmActions/getAccountNotesList/GetAccountNoteListFactory.java b/src/main/java/com/salessparrow/api/lib/crmActions/getAccountNotesList/GetAccountNoteListFactory.java new file mode 100644 index 00000000..11f8e882 --- /dev/null +++ b/src/main/java/com/salessparrow/api/lib/crmActions/getAccountNotesList/GetAccountNoteListFactory.java @@ -0,0 +1,38 @@ +package com.salessparrow.api.lib.crmActions.getAccountNotesList; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.salessparrow.api.domain.User; +import com.salessparrow.api.dto.formatter.GetNotesListFormatterDto; +import com.salessparrow.api.exception.CustomException; +import com.salessparrow.api.lib.errorLib.ErrorObject; +import com.salessparrow.api.lib.globalConstants.UserConstants; + +/** + * GetNotesListFactory is a factory class for the GetNotesList action for the CRM. + */ +@Component +public class GetAccountNoteListFactory { + + @Autowired + private GetSalesforceAccountNotesList getSalesforceNotesList; + + /** + * Get the list of notes for a given account. + * @param user + * @param accountId + * @return GetNotesListFormatterDto + **/ + public GetNotesListFormatterDto getNotesList(User user, String accountId) { + + switch (user.getUserKind()) { + case UserConstants.SALESFORCE_USER_KIND: + return getSalesforceNotesList.getNotesList(user, accountId); + default: + throw new CustomException( + new ErrorObject("l_ca_gnl_gnlf_gnl_1", "something_went_wrong", "Invalid user kind.")); + } + } + +} diff --git a/src/main/java/com/salessparrow/api/lib/crmActions/getNotesList/GetNotesList.java b/src/main/java/com/salessparrow/api/lib/crmActions/getAccountNotesList/GetAccountNotesList.java similarity index 58% rename from src/main/java/com/salessparrow/api/lib/crmActions/getNotesList/GetNotesList.java rename to src/main/java/com/salessparrow/api/lib/crmActions/getAccountNotesList/GetAccountNotesList.java index 7d4ba68e..99155a8b 100644 --- a/src/main/java/com/salessparrow/api/lib/crmActions/getNotesList/GetNotesList.java +++ b/src/main/java/com/salessparrow/api/lib/crmActions/getAccountNotesList/GetAccountNotesList.java @@ -1,4 +1,4 @@ -package com.salessparrow.api.lib.crmActions.getNotesList; +package com.salessparrow.api.lib.crmActions.getAccountNotesList; import org.springframework.stereotype.Component; @@ -9,7 +9,8 @@ * GetNotesList is an interface for the GetNotesList action for the CRM. */ @Component -public interface GetNotesList { - public GetNotesListFormatterDto getNotesList(User user, String accountId); -} +public interface GetAccountNotesList { + + public GetNotesListFormatterDto getNotesList(User user, String accountId); +} diff --git a/src/main/java/com/salessparrow/api/lib/crmActions/getAccountNotesList/GetSalesforceAccountNotesList.java b/src/main/java/com/salessparrow/api/lib/crmActions/getAccountNotesList/GetSalesforceAccountNotesList.java new file mode 100644 index 00000000..f8765dbb --- /dev/null +++ b/src/main/java/com/salessparrow/api/lib/crmActions/getAccountNotesList/GetSalesforceAccountNotesList.java @@ -0,0 +1,172 @@ +package com.salessparrow.api.lib.crmActions.getAccountNotesList; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.salessparrow.api.domain.User; +import com.salessparrow.api.dto.entities.NoteEntity; +import com.salessparrow.api.dto.formatter.GetNotesListFormatterDto; +import com.salessparrow.api.exception.CustomException; +import com.salessparrow.api.lib.Util; +import com.salessparrow.api.lib.errorLib.ErrorObject; +import com.salessparrow.api.lib.globalConstants.SalesforceConstants; +import com.salessparrow.api.lib.httpLib.HttpClient; +import com.salessparrow.api.lib.salesforce.dto.CompositeRequestDto; +import com.salessparrow.api.lib.salesforce.dto.SalesforceGetNoteIdDto; +import com.salessparrow.api.lib.salesforce.dto.SalesforceGetNotesListDto; +import com.salessparrow.api.lib.salesforce.helper.MakeCompositeRequest; +import com.salessparrow.api.lib.salesforce.helper.SalesforceQueryBuilder; + +/** + * GetSalesforceNotesList is a class for the GetNotesList service for the Salesforce CRM. + */ +@Component +public class GetSalesforceAccountNotesList implements GetAccountNotesList { + + @Autowired + private SalesforceConstants salesforceConstants; + + @Autowired + private MakeCompositeRequest makeCompositeRequest; + + /** + * Get the list of notes for a given account + * @param user + * @param accountId + * @return GetNotesListFormatterDto + **/ + public GetNotesListFormatterDto getNotesList(User user, String accountId) { + + String salesforceUserId = user.getExternalUserId(); + + HttpClient.HttpResponse response = notesListIdResponse(accountId, salesforceUserId); + + List ContentDocumentIds = parseNotesId(response.getResponseBody()); + + if (ContentDocumentIds.size() == 0) { + return new GetNotesListFormatterDto(new ArrayList(), new HashMap()); + } + + HttpClient.HttpResponse getNotesResponse = notesListByIdResponse(ContentDocumentIds, salesforceUserId); + + GetNotesListFormatterDto getNotesListFormatterDto = parseResponse(getNotesResponse.getResponseBody()); + + return getNotesListFormatterDto; + } + + /** + * Get the list of notes id for a given account + * @param accountId + * @param salesforceUserId + * @return HttpResponse + **/ + private HttpClient.HttpResponse notesListIdResponse(String accountId, String salesforceUserId) { + SalesforceQueryBuilder salesforceLib = new SalesforceQueryBuilder(); + String documentIdsQuery = salesforceLib.getContentDocumentIdUrl(accountId); + + String documentIdsUrl = salesforceConstants.queryUrlPath() + documentIdsQuery; + + CompositeRequestDto documentIdsCompositeReq = new CompositeRequestDto("GET", documentIdsUrl, + "GetContentDocumentId"); + + List compositeRequests = new ArrayList(); + compositeRequests.add(documentIdsCompositeReq); + + HttpClient.HttpResponse response = makeCompositeRequest.makePostRequest(compositeRequests, salesforceUserId); + + return response; + } + + /** + * Get the list of notes for a given account + * @param documentIds + * @param salesforceUserId + * @return HttpResponse + **/ + private HttpClient.HttpResponse notesListByIdResponse(List documentIds, String salesforceUserId) { + SalesforceQueryBuilder salesforceLib = new SalesforceQueryBuilder(); + String notesQuery = salesforceLib.getNoteListIdUrl(documentIds); + + String notesUrl = salesforceConstants.queryUrlPath() + notesQuery; + + CompositeRequestDto noteCompositeRequest = new CompositeRequestDto("GET", notesUrl, "GetNotesList"); + + List compositeRequests = new ArrayList(); + compositeRequests.add(noteCompositeRequest); + + HttpClient.HttpResponse response = makeCompositeRequest.makePostRequest(compositeRequests, salesforceUserId); + + return response; + } + + /** + * Get the list of notes for a given account + * @param responseBody + * @return List + */ + private List parseNotesId(String responseBody) { + List notesIds = new ArrayList(); + + try { + Util util = new Util(); + JsonNode rootNode = util.getJsonNode(responseBody); + JsonNode recordsNode = rootNode.get("compositeResponse").get(0).get("body").get("records"); + for (JsonNode recordNode : recordsNode) { + ObjectMapper mapper = new ObjectMapper(); + mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); + SalesforceGetNoteIdDto salesforceGetNoteId = mapper.convertValue(recordNode, + SalesforceGetNoteIdDto.class); + notesIds.add(salesforceGetNoteId.getContentDocumentId()); + } + } + catch (Exception e) { + throw new CustomException(new ErrorObject("l_c_gnl_gsnl_1", "something_went_wrong", e.getMessage())); + } + + return notesIds; + } + + /** + * Get the list of notes for a given account + * @param responseBody + * @return GetNotesListFormatterDto + */ + private GetNotesListFormatterDto parseResponse(String responseBody) { + List noteIds = new ArrayList(); + Map noteIdToEntityMap = new HashMap<>(); + + try { + Util util = new Util(); + JsonNode rootNode = util.getJsonNode(responseBody); + JsonNode recordsNode = rootNode.get("compositeResponse").get(0).get("body").get("records"); + for (JsonNode recordNode : recordsNode) { + ObjectMapper mapper = new ObjectMapper(); + mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); + SalesforceGetNotesListDto salesforceGetNotesList = mapper.convertValue(recordNode, + SalesforceGetNotesListDto.class); + NoteEntity noteEntity = salesforceGetNotesList.noteEntity(); + + noteIds.add(noteEntity.getId()); + noteIdToEntityMap.put(noteEntity.getId(), noteEntity); + } + } + catch (Exception e) { + throw new CustomException(new ErrorObject("l_c_gnl_gsnl_2", "something_went_wrong", e.getMessage())); + } + + GetNotesListFormatterDto getNotesListFormatterDto = new GetNotesListFormatterDto(); + getNotesListFormatterDto.setNoteIds(noteIds); + getNotesListFormatterDto.setNoteMapById(noteIdToEntityMap); + + return getNotesListFormatterDto; + } + +} diff --git a/src/main/java/com/salessparrow/api/lib/crmActions/getAccountTasksList/GetAccountTasksListFactory.java b/src/main/java/com/salessparrow/api/lib/crmActions/getAccountTasksList/GetAccountTasksListFactory.java new file mode 100644 index 00000000..d388c065 --- /dev/null +++ b/src/main/java/com/salessparrow/api/lib/crmActions/getAccountTasksList/GetAccountTasksListFactory.java @@ -0,0 +1,44 @@ +package com.salessparrow.api.lib.crmActions.getAccountTasksList; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.salessparrow.api.domain.User; +import com.salessparrow.api.dto.formatter.GetTasksListFormatterDto; +import com.salessparrow.api.exception.CustomException; +import com.salessparrow.api.lib.errorLib.ErrorObject; +import com.salessparrow.api.lib.globalConstants.UserConstants; + +/** + * GetAccountTasksListFactory is a factory class for the GetAccountTasksList action for + * the CRM. + */ +@Component +public class GetAccountTasksListFactory { + + Logger logger = LoggerFactory.getLogger(GetAccountTasksListFactory.class); + + @Autowired + private GetSalesforceAccountTasksList getSalesforceAccountTasksList; + + /** + * Get the list of tasks for a given account. + * @param user + * @param accountId + * @return GetTasksListFormatterDto + **/ + public GetTasksListFormatterDto getAccountTasksList(User user, String accountId) { + logger.info("factory for getAccountTasksList action"); + + switch (user.getUserKind()) { + case UserConstants.SALESFORCE_USER_KIND: + return getSalesforceAccountTasksList.getAccountTasksList(user, accountId); + default: + throw new CustomException( + new ErrorObject("l_ca_gatl_gatlf_gtl_1", "something_went_wrong", "Invalid user kind.")); + } + } + +} diff --git a/src/main/java/com/salessparrow/api/lib/crmActions/getAccountTasksList/GetAccountTasksListInterface.java b/src/main/java/com/salessparrow/api/lib/crmActions/getAccountTasksList/GetAccountTasksListInterface.java new file mode 100644 index 00000000..5fd7c2b3 --- /dev/null +++ b/src/main/java/com/salessparrow/api/lib/crmActions/getAccountTasksList/GetAccountTasksListInterface.java @@ -0,0 +1,17 @@ +package com.salessparrow.api.lib.crmActions.getAccountTasksList; + +import org.springframework.stereotype.Component; + +import com.salessparrow.api.domain.User; +import com.salessparrow.api.dto.formatter.GetTasksListFormatterDto; + +/** + * GetAccountTasksListInterface is an interface for the GetAccountTasksList action for the + * CRM. + */ +@Component +public interface GetAccountTasksListInterface { + + public GetTasksListFormatterDto getTasksList(User user, String accountId); + +} diff --git a/src/main/java/com/salessparrow/api/lib/crmActions/getAccountTasksList/GetSalesforceAccountTasksList.java b/src/main/java/com/salessparrow/api/lib/crmActions/getAccountTasksList/GetSalesforceAccountTasksList.java new file mode 100644 index 00000000..5d29e90d --- /dev/null +++ b/src/main/java/com/salessparrow/api/lib/crmActions/getAccountTasksList/GetSalesforceAccountTasksList.java @@ -0,0 +1,121 @@ +package com.salessparrow.api.lib.crmActions.getAccountTasksList; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.salessparrow.api.domain.User; +import com.salessparrow.api.dto.entities.TaskEntity; +import com.salessparrow.api.dto.formatter.GetTasksListFormatterDto; +import com.salessparrow.api.exception.CustomException; +import com.salessparrow.api.lib.Util; +import com.salessparrow.api.lib.errorLib.ErrorObject; +import com.salessparrow.api.lib.errorLib.ParamErrorObject; +import com.salessparrow.api.lib.globalConstants.SalesforceConstants; +import com.salessparrow.api.lib.httpLib.HttpClient; +import com.salessparrow.api.lib.salesforce.dto.CompositeRequestDto; +import com.salessparrow.api.lib.salesforce.dto.SalesforceGetTasksListDto; +import com.salessparrow.api.lib.salesforce.helper.MakeCompositeRequest; +import com.salessparrow.api.lib.salesforce.helper.SalesforceQueryBuilder; + +/** + * GetSalesforceAccountTasksList is a class for the GetAccountTasksList service for the + * Salesforce CRM. + */ +@Component +public class GetSalesforceAccountTasksList { + + Logger logger = LoggerFactory.getLogger(GetSalesforceAccountTasksList.class); + + @Autowired + private SalesforceConstants salesforceConstants; + + @Autowired + private MakeCompositeRequest makeCompositeRequest; + + /** + * Get the list of tasks for a given account in salesforce + * @param user + * @param accountId + * @return GetTasksListFormatterDto + **/ + public GetTasksListFormatterDto getAccountTasksList(User user, String accountId) { + logger.info("Salesforce getAccountTasksList action called"); + + String salesforceUserId = user.getExternalUserId(); + + SalesforceQueryBuilder salesforceQuery = new SalesforceQueryBuilder(); + String query = salesforceQuery.getAccountTasksQuery(accountId); + + String url = salesforceConstants.queryUrlPath() + query; + + CompositeRequestDto compositeReq = new CompositeRequestDto("GET", url, "GetTasksList"); + + List compositeRequests = new ArrayList(); + compositeRequests.add(compositeReq); + + HttpClient.HttpResponse response = makeCompositeRequest.makePostRequest(compositeRequests, salesforceUserId); + + return parseResponse(response.getResponseBody()); + } + + /** + * Parse Response + * @param responseBody + * @return GetTasksListFormatterDto + **/ + public GetTasksListFormatterDto parseResponse(String responseBody) { + + List taskIds = new ArrayList(); + Map taskIdToEntityMap = new HashMap<>(); + + Util util = new Util(); + JsonNode rootNode = util.getJsonNode(responseBody); + + JsonNode getTasksCompositeResponse = rootNode.get("compositeResponse").get(0); + Integer getTasksStatusCode = getTasksCompositeResponse.get("httpStatusCode").asInt(); + + if (getTasksStatusCode != 200 && getTasksStatusCode != 201) { + String errorBody = getTasksCompositeResponse.get("body").asText(); + + if (getTasksStatusCode == 400) { + throw new CustomException( + new ParamErrorObject("l_ca_gatl_gsatl_pr_1", errorBody, Arrays.asList("invalid_account_id"))); + } + else { + throw new CustomException(new ErrorObject("l_ca_gatl_gsatl_pr_2", "something_went_wrong", errorBody)); + } + } + + JsonNode recordsNode = rootNode.get("compositeResponse").get(0).get("body").get("records"); + ; + + for (JsonNode recordNode : recordsNode) { + ObjectMapper mapper = new ObjectMapper(); + mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); + SalesforceGetTasksListDto salesforceGetTasksListDto = mapper.convertValue(recordNode, + SalesforceGetTasksListDto.class); + TaskEntity taskEntity = salesforceGetTasksListDto.taskEntity(); + + taskIds.add(taskEntity.getId()); + taskIdToEntityMap.put(taskEntity.getId(), taskEntity); + } + + GetTasksListFormatterDto getTasksListFormatterDto = new GetTasksListFormatterDto(); + getTasksListFormatterDto.setTaskMapById(taskIdToEntityMap); + getTasksListFormatterDto.setTaskIds(taskIds); + + return getTasksListFormatterDto; + } + +} diff --git a/src/main/java/com/salessparrow/api/lib/crmActions/getAccounts/GetAccounts.java b/src/main/java/com/salessparrow/api/lib/crmActions/getAccounts/GetAccounts.java index b6a44704..64d9648e 100644 --- a/src/main/java/com/salessparrow/api/lib/crmActions/getAccounts/GetAccounts.java +++ b/src/main/java/com/salessparrow/api/lib/crmActions/getAccounts/GetAccounts.java @@ -11,5 +11,6 @@ @Component public interface GetAccounts { - public GetAccountsFormatterDto getAccounts(User user, String searchTerm); + public GetAccountsFormatterDto getAccounts(User user, String searchTerm, String viewKind, int offset); + } diff --git a/src/main/java/com/salessparrow/api/lib/crmActions/getAccounts/GetAccountsFactory.java b/src/main/java/com/salessparrow/api/lib/crmActions/getAccounts/GetAccountsFactory.java index 4c29d58f..90be5c5b 100644 --- a/src/main/java/com/salessparrow/api/lib/crmActions/getAccounts/GetAccountsFactory.java +++ b/src/main/java/com/salessparrow/api/lib/crmActions/getAccounts/GetAccountsFactory.java @@ -14,28 +14,25 @@ */ @Component public class GetAccountsFactory { - @Autowired - private GetSalesforceAccounts getSalesforceAccounts; - /** - * Get the list of accounts for a given searchterm. - * - * @param user - * @param searchTerm - * - * @return GetAccountsFormatterDto - **/ - public GetAccountsFormatterDto getAccounts(User user, String searchTerm) { + @Autowired + private GetSalesforceAccounts getSalesforceAccounts; + + /** + * Get the list of accounts for a given searchterm. + * @param user + * @param searchTerm + * @return GetAccountsFormatterDto + **/ + public GetAccountsFormatterDto getAccounts(User user, String searchTerm, String viewKind, int offset) { + + switch (user.getUserKind()) { + case UserConstants.SALESFORCE_USER_KIND: + return getSalesforceAccounts.getAccounts(user, searchTerm, viewKind, offset); + default: + throw new CustomException( + new ErrorObject("l_ca_ga_gaf_ga_1", "something_went_wrong", "Invalid user kind.")); + } + } - switch(user.getUserKind()) { - case UserConstants.SALESFORCE_USER_KIND: - return getSalesforceAccounts.getAccounts(user, searchTerm); - default: - throw new CustomException( - new ErrorObject( - "l_ca_ga_gaf_ga_1", - "something_went_wrong", - "Invalid user kind.")); - } - } } diff --git a/src/main/java/com/salessparrow/api/lib/crmActions/getAccounts/GetSalesforceAccounts.java b/src/main/java/com/salessparrow/api/lib/crmActions/getAccounts/GetSalesforceAccounts.java index 73529e6e..d1ef10e1 100644 --- a/src/main/java/com/salessparrow/api/lib/crmActions/getAccounts/GetSalesforceAccounts.java +++ b/src/main/java/com/salessparrow/api/lib/crmActions/getAccounts/GetSalesforceAccounts.java @@ -5,6 +5,8 @@ import java.util.List; import java.util.Map; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -12,15 +14,20 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.salessparrow.api.domain.User; +import com.salessparrow.api.dto.entities.AccountContactAssociationsEntity; import com.salessparrow.api.dto.entities.AccountEntity; +import com.salessparrow.api.dto.entities.ContactEntity; import com.salessparrow.api.dto.formatter.GetAccountsFormatterDto; import com.salessparrow.api.exception.CustomException; +import com.salessparrow.api.lib.UserLoginCookieAuth; import com.salessparrow.api.lib.Util; import com.salessparrow.api.lib.errorLib.ErrorObject; +import com.salessparrow.api.lib.globalConstants.AccountConstants; import com.salessparrow.api.lib.globalConstants.SalesforceConstants; import com.salessparrow.api.lib.httpLib.HttpClient; import com.salessparrow.api.lib.salesforce.dto.CompositeRequestDto; import com.salessparrow.api.lib.salesforce.dto.SalesforceAccountDto; +import com.salessparrow.api.lib.salesforce.dto.SalesforceContactDto; import com.salessparrow.api.lib.salesforce.helper.MakeCompositeRequest; import com.salessparrow.api.lib.salesforce.helper.SalesforceQueryBuilder; @@ -28,80 +35,117 @@ * GetSalesforceAccounts is a class for the GetAccounts service for the Salesforce CRM. **/ @Component -public class GetSalesforceAccounts implements GetAccounts{ - @Autowired - private SalesforceConstants salesforceConstants; - - @Autowired - private MakeCompositeRequest makeCompositeRequest; - - /** - * Get the list of accounts for a given search term - * - * @param user - * @param searchTerm - * - * @return GetAccountsFormatterDto - **/ - public GetAccountsFormatterDto getAccounts(User user, String searchTerm) { - String salesforceUserId = user.getExternalUserId(); - - SalesforceQueryBuilder salesforceQuery = new SalesforceQueryBuilder(); - String query = salesforceQuery.getAccountsQuery(searchTerm); - - String url = salesforceConstants.queryUrlPath() + query; - - CompositeRequestDto compositeReq = new CompositeRequestDto("GET", url, "getAccounts"); - - List compositeRequests = new ArrayList(); - compositeRequests.add(compositeReq); - - HttpClient.HttpResponse response = makeCompositeRequest.makePostRequest(compositeRequests, salesforceUserId); - - return parseResponse(response.getResponseBody()); - } - - /** - * Parse Response - * - * @param responseBody - * - * @return GetAccountsFormatterDto - **/ - public GetAccountsFormatterDto parseResponse(String responseBody) { - - List accountIds = new ArrayList(); - Map accountIdToEntityMap = new HashMap<>(); - - Util util = new Util(); - JsonNode rootNode = util.getJsonNode(responseBody); - - JsonNode httpStatusCodeNode = rootNode.get("compositeResponse").get(0).get("httpStatusCode"); - - if (httpStatusCodeNode.asInt() != 200 && httpStatusCodeNode.asInt() != 201) { - throw new CustomException( - new ErrorObject( - "l_ca_ga_gsa_pr_1", - "bad_request", - "Error in fetching accounts from salesforce")); - } - - JsonNode recordsNode = rootNode.get("compositeResponse").get(0).get("body").get("records"); - - for (JsonNode recordNode : recordsNode) { - ObjectMapper mapper = new ObjectMapper(); - mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); - SalesforceAccountDto salesforceAccount = mapper.convertValue(recordNode, SalesforceAccountDto.class); - AccountEntity accountEntity = salesforceAccount.getAccountEntity(); - - accountIds.add(accountEntity.getId()); - accountIdToEntityMap.put(accountEntity.getId(), accountEntity); - } - - GetAccountsFormatterDto getAccountsResponse = new GetAccountsFormatterDto(); - getAccountsResponse.setAccountMapById(accountIdToEntityMap); - getAccountsResponse.setAccountIds(accountIds); - - return getAccountsResponse; - } -} +public class GetSalesforceAccounts implements GetAccounts { + + @Autowired + private SalesforceConstants salesforceConstants; + + @Autowired + private MakeCompositeRequest makeCompositeRequest; + + @Autowired + private Util util; + + Logger logger = LoggerFactory.getLogger(UserLoginCookieAuth.class); + + /** + * Get the list of accounts for a given search term + * @param user + * @param searchTerm + * @return GetAccountsFormatterDto + **/ + public GetAccountsFormatterDto getAccounts(User user, String searchTerm, String viewKind, int offset) { + String salesforceUserId = user.getExternalUserId(); + + SalesforceQueryBuilder salesforceQuery = new SalesforceQueryBuilder(); + String query = null; + + if (viewKind.equals(AccountConstants.FEED_VIEW_KIND)) { + logger.info("View kind is feed"); + query = salesforceQuery.getAccountFeedQuery(AccountConstants.PAGINATION_LIMIT, offset); + } + else if (viewKind.equals(AccountConstants.BASIC_VIEW_KIND)) { + logger.info("View kind is basic"); + query = salesforceQuery.getAccountsQuery(searchTerm); + } + else { + throw new CustomException( + new ErrorObject("l_ca_ga_gsa_ga_1", "something_went_wrong", "Invalid view kind.")); + } + + String url = salesforceConstants.queryUrlPath() + query; + + CompositeRequestDto compositeReq = new CompositeRequestDto("GET", url, "getAccounts"); + + List compositeRequests = new ArrayList(); + compositeRequests.add(compositeReq); + + HttpClient.HttpResponse response = makeCompositeRequest.makePostRequest(compositeRequests, salesforceUserId); + + return parseResponse(response.getResponseBody()); + } + + /** + * Parse Response + * @param responseBody + * @return GetAccountsFormatterDto + **/ + public GetAccountsFormatterDto parseResponse(String responseBody) { + + List accountIds = new ArrayList(); + Map accountIdToEntityMap = new HashMap<>(); + Map contactMapById = new HashMap<>(); + Map accountContactAssociationsMapById = new HashMap<>(); + + JsonNode rootNode = util.getJsonNode(responseBody); + + JsonNode httpStatusCodeNode = rootNode.get("compositeResponse").get(0).get("httpStatusCode"); + + if (httpStatusCodeNode.asInt() != 200 && httpStatusCodeNode.asInt() != 201) { + throw new CustomException(new ErrorObject("l_ca_ga_gsa_pr_1", "something_went_wrong", + "Error in fetching accounts from salesforce")); + } + + JsonNode recordsNode = rootNode.get("compositeResponse").get(0).get("body").get("records"); + + for (JsonNode recordNode : recordsNode) { + ObjectMapper mapper = new ObjectMapper(); + mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); + SalesforceAccountDto salesforceAccount = mapper.convertValue(recordNode, SalesforceAccountDto.class); + + AccountEntity accountEntity = salesforceAccount.getAccountEntity(); + accountIds.add(accountEntity.getId()); + accountIdToEntityMap.put(accountEntity.getId(), accountEntity); + + if (salesforceAccount.getContacts() != null && salesforceAccount.getContacts().getRecords() != null + && salesforceAccount.getContacts().getRecords().size() > 0) { + handleContacts(salesforceAccount, contactMapById, accountContactAssociationsMapById); + } + } + + GetAccountsFormatterDto getAccountsResponse = new GetAccountsFormatterDto(); + getAccountsResponse.setAccountMapById(accountIdToEntityMap); + getAccountsResponse.setAccountIds(accountIds); + getAccountsResponse.setContactMapById(contactMapById); + getAccountsResponse.setAccountContactAssociationsMapById(accountContactAssociationsMapById); + + return getAccountsResponse; + } + + private void handleContacts(SalesforceAccountDto salesforceAccount, Map contactMapById, + Map accountContactAssociationsMapById) { + List contactIds = new ArrayList(); + + for (SalesforceContactDto contact : salesforceAccount.getContacts().getRecords()) { + ContactEntity contactEntity = contact.getContactEntity(); + contactMapById.put(contactEntity.getId(), contactEntity); + contactIds.add(contactEntity.getId()); + } + + AccountContactAssociationsEntity accountContactAssociationsEntity = new AccountContactAssociationsEntity(); + accountContactAssociationsEntity.setContactIds(contactIds); + accountContactAssociationsMapById.put(salesforceAccount.getAccountEntity().getId(), + accountContactAssociationsEntity); + } + +} \ No newline at end of file diff --git a/src/main/java/com/salessparrow/api/lib/crmActions/getCrmOrganizationUsers/GetCrmOrganizationUsers.java b/src/main/java/com/salessparrow/api/lib/crmActions/getCrmOrganizationUsers/GetCrmOrganizationUsers.java new file mode 100644 index 00000000..4263a9b5 --- /dev/null +++ b/src/main/java/com/salessparrow/api/lib/crmActions/getCrmOrganizationUsers/GetCrmOrganizationUsers.java @@ -0,0 +1,17 @@ +package com.salessparrow.api.lib.crmActions.getCrmOrganizationUsers; + +import org.springframework.stereotype.Component; + +import com.salessparrow.api.domain.User; +import com.salessparrow.api.dto.formatter.GetCrmOrganizationUsersFormatterDto; + +/** + * GetCrmOrganizationUsers interface for the getCrmOrganizationUsers action. + * + */ +@Component +public interface GetCrmOrganizationUsers { + + public GetCrmOrganizationUsersFormatterDto getCrmOrganizationUsers(User user, String searchTerm); + +} diff --git a/src/main/java/com/salessparrow/api/lib/crmActions/getCrmOrganizationUsers/GetCrmOrganizationUsersFactory.java b/src/main/java/com/salessparrow/api/lib/crmActions/getCrmOrganizationUsers/GetCrmOrganizationUsersFactory.java new file mode 100644 index 00000000..bf819926 --- /dev/null +++ b/src/main/java/com/salessparrow/api/lib/crmActions/getCrmOrganizationUsers/GetCrmOrganizationUsersFactory.java @@ -0,0 +1,43 @@ +package com.salessparrow.api.lib.crmActions.getCrmOrganizationUsers; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.salessparrow.api.domain.User; +import com.salessparrow.api.dto.formatter.GetCrmOrganizationUsersFormatterDto; +import com.salessparrow.api.exception.CustomException; +import com.salessparrow.api.lib.errorLib.ErrorObject; +import com.salessparrow.api.lib.globalConstants.UserConstants; + +/** + * GetCrmOrganizationUsersFactory class for the getCrmOrganizationUsers action. + * + */ +@Component +public class GetCrmOrganizationUsersFactory { + + Logger logger = LoggerFactory.getLogger(GetCrmOrganizationUsersFactory.class); + + @Autowired + private GetSalesforceCrmOrganizationUsers getSalesforceCrmOrganizationUsers; + + /** + * getCrmOrganizationUsers method for the getCrmOrganizationUsers action. + * @param user + * @param searchTerm + * @return GetCrmOrganizationUsersFormatterDto + */ + public GetCrmOrganizationUsersFormatterDto getCrmOrganizationUsers(User user, String searchTerm) { + switch (user.getUserKind()) { + case UserConstants.SALESFORCE_USER_KIND: + logger.info("Executing salesforce get CrmOrganizationUser Service"); + return getSalesforceCrmOrganizationUsers.getCrmOrganizationUsers(user, searchTerm); + default: + throw new CustomException( + new ErrorObject("l_ca_gcou_gcouf_gcou_1", "something_went_wrong", "Invalid user kind.")); + } + } + +} diff --git a/src/main/java/com/salessparrow/api/lib/crmActions/getCrmOrganizationUsers/GetSalesforceCrmOrganizationUsers.java b/src/main/java/com/salessparrow/api/lib/crmActions/getCrmOrganizationUsers/GetSalesforceCrmOrganizationUsers.java new file mode 100644 index 00000000..b911a6fa --- /dev/null +++ b/src/main/java/com/salessparrow/api/lib/crmActions/getCrmOrganizationUsers/GetSalesforceCrmOrganizationUsers.java @@ -0,0 +1,110 @@ +package com.salessparrow.api.lib.crmActions.getCrmOrganizationUsers; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.salessparrow.api.domain.User; +import com.salessparrow.api.dto.entities.CrmOrganizationUserEntity; +import com.salessparrow.api.dto.formatter.GetCrmOrganizationUsersFormatterDto; +import com.salessparrow.api.exception.CustomException; +import com.salessparrow.api.lib.Util; +import com.salessparrow.api.lib.errorLib.ErrorObject; +import com.salessparrow.api.lib.globalConstants.SalesforceConstants; +import com.salessparrow.api.lib.httpLib.HttpClient; +import com.salessparrow.api.lib.salesforce.dto.CompositeRequestDto; +import com.salessparrow.api.lib.salesforce.dto.SalesforceCrmOrganizationUserDto; +import com.salessparrow.api.lib.salesforce.helper.MakeCompositeRequest; +import com.salessparrow.api.lib.salesforce.helper.SalesforceQueryBuilder; + +/** + * GetCrmOrganizationUsersFactory class for the getCrmOrganizationUsers action. + * + */ +@Component +public class GetSalesforceCrmOrganizationUsers implements GetCrmOrganizationUsers { + + Logger logger = LoggerFactory.getLogger(GetSalesforceCrmOrganizationUsers.class); + + @Autowired + private SalesforceConstants salesforceConstants; + + @Autowired + private MakeCompositeRequest makeCompositeRequest; + + /** + * getCrmOrganizationUsers method for the getCrmOrganizationUsers action. + * @param user + * @param searchTerm + * @return GetCrmOrganizationUsersFormatterDto + */ + public GetCrmOrganizationUsersFormatterDto getCrmOrganizationUsers(User user, String searchTerm) { + String salesforceUserId = user.getExternalUserId(); + + SalesforceQueryBuilder salesforceQuery = new SalesforceQueryBuilder(); + String query = salesforceQuery.getCrmOrganizationUsersQuery(searchTerm); + + String url = salesforceConstants.queryUrlPath() + query; + + CompositeRequestDto compositeReq = new CompositeRequestDto("GET", url, "getCrmOrganizationUsers"); + + List compositeRequests = new ArrayList(); + compositeRequests.add(compositeReq); + + HttpClient.HttpResponse response = makeCompositeRequest.makePostRequest(compositeRequests, salesforceUserId); + + return parseResponse(response.getResponseBody()); + } + + /** + * parseResponse method for the getCrmOrganizationUsers action. + * @param responseBody + * @return GetCrmOrganizationUsersFormatterDto + */ + private GetCrmOrganizationUsersFormatterDto parseResponse(String responseBody) { + List crmOrganizationUserIds = new ArrayList(); + Map crmOrganizationUserMap = new HashMap(); + + logger.info("Parsing response from salesforce"); + + Util util = new Util(); + JsonNode rootNode = util.getJsonNode(responseBody); + + JsonNode httpStatusCodeNode = rootNode.get("compositeResponse").get(0).get("httpStatusCode"); + + if (httpStatusCodeNode.asInt() != 200 && httpStatusCodeNode.asInt() != 201) { + throw new CustomException( + new ErrorObject("l_ca_ga_gsa_pr_1", "bad_request", "Error in fetching accounts from salesforce")); + } + + JsonNode recordsNode = rootNode.get("compositeResponse").get(0).get("body").get("records"); + + for (JsonNode recordNode : recordsNode) { + ObjectMapper mapper = new ObjectMapper(); + mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); + SalesforceCrmOrganizationUserDto salesforceCrmOrganization = mapper.convertValue(recordNode, + SalesforceCrmOrganizationUserDto.class); + CrmOrganizationUserEntity accountCrmOrganizationUser = salesforceCrmOrganization + .getCrmOrganizationUserEntity(); + + crmOrganizationUserIds.add(accountCrmOrganizationUser.getId()); + crmOrganizationUserMap.put(accountCrmOrganizationUser.getId(), accountCrmOrganizationUser); + } + + GetCrmOrganizationUsersFormatterDto getCrmOrganizationUsersResponse = new GetCrmOrganizationUsersFormatterDto(); + getCrmOrganizationUsersResponse.setCrmOrganizationUserIds(crmOrganizationUserIds); + getCrmOrganizationUsersResponse.setCrmOrganizationUserMapById(crmOrganizationUserMap); + + return getCrmOrganizationUsersResponse; + } + +} diff --git a/src/main/java/com/salessparrow/api/lib/crmActions/getNoteDetails/GetNoteDetailsFactory.java b/src/main/java/com/salessparrow/api/lib/crmActions/getNoteDetails/GetNoteDetailsFactory.java deleted file mode 100644 index 41754968..00000000 --- a/src/main/java/com/salessparrow/api/lib/crmActions/getNoteDetails/GetNoteDetailsFactory.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.salessparrow.api.lib.crmActions.getNoteDetails; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -import com.salessparrow.api.domain.User; -import com.salessparrow.api.dto.formatter.GetNoteDetailsFormatterDto; -import com.salessparrow.api.exception.CustomException; -import com.salessparrow.api.lib.errorLib.ErrorObject; -import com.salessparrow.api.lib.globalConstants.UserConstants; - -/** - * GetNoteDetailsFactory is a factory class for the GetNoteDetails action for the CRM. - */ -@Component -public class GetNoteDetailsFactory { - @Autowired - GetSalesforceNoteDetails getSalesforceNoteDetails; - - /** - * getNoteDetails is a method that returns the details of a note. - * - * @param user - * @param noteId - * - * @return GetNoteDetailsFormatterDto - */ - public GetNoteDetailsFormatterDto getNoteDetails(User user, String noteId) { - - switch(user.getUserKind()) { - case UserConstants.SALESFORCE_USER_KIND: - return getSalesforceNoteDetails.getNoteDetails(user, noteId); - default: - throw new CustomException( - new ErrorObject( - "l_ca_gnd_gndf_gnd_1", - "something_went_wrong", - "Invalid user kind.")); - } - } -} diff --git a/src/main/java/com/salessparrow/api/lib/crmActions/getNoteDetails/GetSalesforceNoteDetails.java b/src/main/java/com/salessparrow/api/lib/crmActions/getNoteDetails/GetSalesforceNoteDetails.java deleted file mode 100644 index bac6f1ea..00000000 --- a/src/main/java/com/salessparrow/api/lib/crmActions/getNoteDetails/GetSalesforceNoteDetails.java +++ /dev/null @@ -1,132 +0,0 @@ -package com.salessparrow.api.lib.crmActions.getNoteDetails; - -import java.util.ArrayList; -import java.util.List; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.salessparrow.api.domain.User; -import com.salessparrow.api.dto.entities.NoteDetailEntity; -import com.salessparrow.api.dto.formatter.GetNoteDetailsFormatterDto; -import com.salessparrow.api.exception.CustomException; -import com.salessparrow.api.lib.Util; -import com.salessparrow.api.lib.errorLib.ErrorObject; -import com.salessparrow.api.lib.globalConstants.SalesforceConstants; -import com.salessparrow.api.lib.httpLib.HttpClient; -import com.salessparrow.api.lib.salesforce.dto.CompositeRequestDto; -import com.salessparrow.api.lib.salesforce.helper.MakeCompositeRequest; -import com.salessparrow.api.lib.salesforce.dto.SalesforceGetNoteDetailsDto; -import com.salessparrow.api.lib.salesforce.wrappers.SalesforceGetNoteContent; -import com.salessparrow.api.lib.salesforce.helper.SalesforceQueryBuilder; - -@Component -public class GetSalesforceNoteDetails implements GetNoteDetails { - - @Autowired - private SalesforceConstants salesforceConstants; - - @Autowired - private MakeCompositeRequest makeCompositeRequest; - - @Autowired - private SalesforceGetNoteContent salesforceGetNoteContent; - - /** - * Get the details of a note - * - * @param user - * @param noteId - * - * @return GetNoteDetailsFormatterDto - **/ - public GetNoteDetailsFormatterDto getNoteDetails(User user, String noteId) { - - String salesforceUserId = user.getExternalUserId(); - - HttpClient.HttpResponse noteDetailsResponse = notesResponse(noteId, salesforceUserId); - - HttpClient.HttpResponse noteContentResponse = salesforceGetNoteContent.getNoteContent(noteId, salesforceUserId); - - GetNoteDetailsFormatterDto noteDetailsFormatterDto = parseResponse(noteDetailsResponse.getResponseBody(), noteContentResponse.getResponseBody()); - - return noteDetailsFormatterDto; - - } - - /** - * Get the list of notes for a given account - * - * @param documentIds - * @param salesforceUserId - * - * @return HttpResponse - **/ - private HttpClient.HttpResponse notesResponse(String noteId, String salesforceUserId) { - SalesforceQueryBuilder salesforceLib = new SalesforceQueryBuilder(); - String notesQuery = salesforceLib.getNoteDetailsUrl(noteId); - - String notesUrl = salesforceConstants.queryUrlPath() + notesQuery; - - CompositeRequestDto noteCompositeRequest = new CompositeRequestDto("GET", notesUrl, "GetNotesList"); - - List compositeRequests = new ArrayList(); - compositeRequests.add(noteCompositeRequest); - - HttpClient.HttpResponse response = makeCompositeRequest.makePostRequest(compositeRequests, salesforceUserId); - - return response; - } - - /** - * Format the response of the note details - * - * @param noteDetailsResponse - * @param noteContentResponse - * - * @return GetNoteDetailsFormatterDto - */ - private GetNoteDetailsFormatterDto parseResponse(String noteDetailsResponse, String noteContentResponse) { - NoteDetailEntity noteDetailEntity = new NoteDetailEntity(); - try { - Util util = new Util(); - JsonNode rootNode = util.getJsonNode(noteDetailsResponse); - JsonNode recordsNode = rootNode.get("compositeResponse").get(0).get("body").get("records"); - - for (JsonNode recordNode : recordsNode) { - ObjectMapper mapper = new ObjectMapper(); - mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); - SalesforceGetNoteDetailsDto salesforceGetNotesList = mapper.convertValue(recordNode, SalesforceGetNoteDetailsDto.class); - noteDetailEntity = salesforceGetNotesList.noteDetailEntity(noteContentResponse); - } - if(recordsNode.size() == 0) { - throw new CustomException( - new ErrorObject( - "l_c_gnd_gsnd_1", - "something_went_wrong", - "Note not found" - ) - ); - } - } catch (Exception e) { - throw new CustomException( - new ErrorObject( - "l_c_gnd_gsnd_2", - "something_went_wrong", - e.getMessage() - ) - ); - } - - GetNoteDetailsFormatterDto getNoteDetailsFormatterDto = new GetNoteDetailsFormatterDto( - noteDetailEntity - ); - - return getNoteDetailsFormatterDto; - - } - -} diff --git a/src/main/java/com/salessparrow/api/lib/crmActions/getNotesList/GetNoteListFactory.java b/src/main/java/com/salessparrow/api/lib/crmActions/getNotesList/GetNoteListFactory.java deleted file mode 100644 index 3556e5db..00000000 --- a/src/main/java/com/salessparrow/api/lib/crmActions/getNotesList/GetNoteListFactory.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.salessparrow.api.lib.crmActions.getNotesList; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -import com.salessparrow.api.domain.User; -import com.salessparrow.api.dto.formatter.GetNotesListFormatterDto; -import com.salessparrow.api.exception.CustomException; -import com.salessparrow.api.lib.errorLib.ErrorObject; -import com.salessparrow.api.lib.globalConstants.UserConstants; - -/** - * GetNotesListFactory is a factory class for the GetNotesList action for the - * CRM. - */ -@Component -public class GetNoteListFactory { - @Autowired - private GetSalesforceNotesList getSalesforceNotesList; - - /** - * Get the list of notes for a given account. - * - * @param user - * @param accountId - * - * @return GetNotesListFormatterDto - **/ - public GetNotesListFormatterDto getNotesList(User user, String accountId) { - - switch(user.getUserKind()) { - case UserConstants.SALESFORCE_USER_KIND: - return getSalesforceNotesList.getNotesList(user, accountId); - default: - throw new CustomException( - new ErrorObject( - "l_ca_gnl_gnlf_gnl_1", - "something_went_wrong", - "Invalid user kind.")); - } - } -} diff --git a/src/main/java/com/salessparrow/api/lib/crmActions/getNotesList/GetSalesforceNotesList.java b/src/main/java/com/salessparrow/api/lib/crmActions/getNotesList/GetSalesforceNotesList.java deleted file mode 100644 index c6bb9ba9..00000000 --- a/src/main/java/com/salessparrow/api/lib/crmActions/getNotesList/GetSalesforceNotesList.java +++ /dev/null @@ -1,190 +0,0 @@ -package com.salessparrow.api.lib.crmActions.getNotesList; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.salessparrow.api.domain.User; -import com.salessparrow.api.dto.entities.NoteEntity; -import com.salessparrow.api.dto.formatter.GetNotesListFormatterDto; -import com.salessparrow.api.exception.CustomException; -import com.salessparrow.api.lib.Util; -import com.salessparrow.api.lib.errorLib.ErrorObject; -import com.salessparrow.api.lib.globalConstants.SalesforceConstants; -import com.salessparrow.api.lib.httpLib.HttpClient; -import com.salessparrow.api.lib.salesforce.dto.CompositeRequestDto; -import com.salessparrow.api.lib.salesforce.dto.SalesforceGetNoteIdDto; -import com.salessparrow.api.lib.salesforce.dto.SalesforceGetNotesListDto; -import com.salessparrow.api.lib.salesforce.helper.MakeCompositeRequest; -import com.salessparrow.api.lib.salesforce.helper.SalesforceQueryBuilder; - -/** - * GetSalesforceNotesList is a class for the GetNotesList service for the Salesforce CRM. - */ -@Component -public class GetSalesforceNotesList implements GetNotesList{ - @Autowired - private SalesforceConstants salesforceConstants; - - @Autowired - private MakeCompositeRequest makeCompositeRequest; - - /** - * Get the list of notes for a given account - * @param user - * @param accountId - * - * @return GetNotesListFormatterDto - **/ - public GetNotesListFormatterDto getNotesList(User user,String accountId) { - - String salesforceUserId = user.getExternalUserId(); - - HttpClient.HttpResponse response = notesListIdResponse(accountId, salesforceUserId); - - List ContentDocumentIds = parseNotesId(response.getResponseBody()); - - if(ContentDocumentIds.size() == 0) { - return new GetNotesListFormatterDto( - new ArrayList(), - new HashMap() - ); - } - - HttpClient.HttpResponse getNotesResponse = notesListByIdResponse(ContentDocumentIds, salesforceUserId); - - GetNotesListFormatterDto getNotesListFormatterDto = parseResponse(getNotesResponse.getResponseBody()); - - return getNotesListFormatterDto; - } - - /** - * Get the list of notes id for a given account - * @param accountId - * @param salesforceUserId - * - * @return HttpResponse - **/ - private HttpClient.HttpResponse notesListIdResponse(String accountId, String salesforceUserId) { - SalesforceQueryBuilder salesforceLib = new SalesforceQueryBuilder(); - String documentIdsQuery = salesforceLib.getContentDocumentIdUrl(accountId); - - String documentIdsUrl = salesforceConstants.queryUrlPath() + documentIdsQuery; - - CompositeRequestDto documentIdsCompositeReq = new CompositeRequestDto("GET", documentIdsUrl, "GetContentDocumentId"); - - List compositeRequests = new ArrayList(); - compositeRequests.add(documentIdsCompositeReq); - - HttpClient.HttpResponse response = makeCompositeRequest.makePostRequest(compositeRequests, salesforceUserId ); - - return response; - } - - /** - * Get the list of notes for a given account - * @param documentIds - * @param salesforceUserId - * - * @return HttpResponse - **/ - private HttpClient.HttpResponse notesListByIdResponse(List documentIds, String salesforceUserId) { - SalesforceQueryBuilder salesforceLib = new SalesforceQueryBuilder(); - String notesQuery = salesforceLib.getNoteListIdUrl(documentIds); - - String notesUrl = salesforceConstants.queryUrlPath() + notesQuery; - - CompositeRequestDto noteCompositeRequest = new CompositeRequestDto("GET", notesUrl, "GetNotesList"); - - List compositeRequests = new ArrayList(); - compositeRequests.add(noteCompositeRequest); - - HttpClient.HttpResponse response = makeCompositeRequest.makePostRequest(compositeRequests, salesforceUserId ); - - return response; - } - - - - /** - * Get the list of notes for a given account - * - * @param responseBody - * - * @return List - */ - private List parseNotesId(String responseBody) { - List notesIds = new ArrayList(); - - try { - Util util = new Util(); - JsonNode rootNode = util.getJsonNode(responseBody); - JsonNode recordsNode = rootNode.get("compositeResponse").get(0).get("body").get("records"); - for (JsonNode recordNode : recordsNode) { - ObjectMapper mapper = new ObjectMapper(); - mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); - SalesforceGetNoteIdDto salesforceGetNoteId = mapper.convertValue(recordNode, SalesforceGetNoteIdDto.class); - notesIds.add(salesforceGetNoteId.getContentDocumentId()); - } - } catch (Exception e) { - throw new CustomException( - new ErrorObject( - "l_c_gnl_gsnl_1", - "something_went_wrong", - e.getMessage() - ) - ); - } - - return notesIds; - } - - /** - * Get the list of notes for a given account - * - * @param responseBody - * - * @return GetNotesListFormatterDto - */ - private GetNotesListFormatterDto parseResponse(String responseBody){ - List noteIds = new ArrayList(); - Map noteIdToEntityMap = new HashMap<>(); - - try { - Util util = new Util(); - JsonNode rootNode = util.getJsonNode(responseBody); - JsonNode recordsNode = rootNode.get("compositeResponse").get(0).get("body").get("records"); - for (JsonNode recordNode : recordsNode) { - ObjectMapper mapper = new ObjectMapper(); - mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); - SalesforceGetNotesListDto salesforceGetNotesList = mapper.convertValue(recordNode, SalesforceGetNotesListDto.class); - NoteEntity noteEntity = salesforceGetNotesList.noteEntity(); - - noteIds.add(noteEntity.getId()); - noteIdToEntityMap.put(noteEntity.getId(), noteEntity); - } - } catch (Exception e) { - throw new CustomException( - new ErrorObject( - "l_c_gnl_gsnl_2", - "something_went_wrong", - e.getMessage() - ) - ); - } - - GetNotesListFormatterDto getNotesListFormatterDto = new GetNotesListFormatterDto(); - getNotesListFormatterDto.setNoteIds(noteIds); - getNotesListFormatterDto.setNoteMapById(noteIdToEntityMap); - - return getNotesListFormatterDto; - } - -} diff --git a/src/main/java/com/salessparrow/api/lib/customAnnotations/ValidDateFormat.java b/src/main/java/com/salessparrow/api/lib/customAnnotations/ValidDateFormat.java new file mode 100644 index 00000000..298ed763 --- /dev/null +++ b/src/main/java/com/salessparrow/api/lib/customAnnotations/ValidDateFormat.java @@ -0,0 +1,20 @@ +package com.salessparrow.api.lib.customAnnotations; + +import jakarta.validation.Constraint; +import jakarta.validation.Payload; +import java.lang.annotation.*; + +import com.salessparrow.api.lib.validators.DateFormatValidator; + +@Target({ ElementType.FIELD }) +@Retention(RetentionPolicy.RUNTIME) +@Constraint(validatedBy = DateFormatValidator.class) +public @interface ValidDateFormat { + + String message() default "Invalid Date Format"; + + Class[] groups() default {}; + + Class[] payload() default {}; + +} \ No newline at end of file diff --git a/src/main/java/com/salessparrow/api/lib/customAnnotations/ValidRedirectUri.java b/src/main/java/com/salessparrow/api/lib/customAnnotations/ValidRedirectUri.java index fe671480..c6dd0201 100644 --- a/src/main/java/com/salessparrow/api/lib/customAnnotations/ValidRedirectUri.java +++ b/src/main/java/com/salessparrow/api/lib/customAnnotations/ValidRedirectUri.java @@ -10,9 +10,11 @@ @Retention(RetentionPolicy.RUNTIME) @Constraint(validatedBy = RedirectUriValidator.class) public @interface ValidRedirectUri { - String message() default "Invalid redirect URI"; - Class[] groups() default {}; + String message() default "Invalid redirect URI"; + + Class[] groups() default {}; + + Class[] payload() default {}; - Class[] payload() default {}; } \ No newline at end of file diff --git a/src/main/java/com/salessparrow/api/lib/errorLib/ErrorConfig.java b/src/main/java/com/salessparrow/api/lib/errorLib/ErrorConfig.java index 998610ec..60a78859 100644 --- a/src/main/java/com/salessparrow/api/lib/errorLib/ErrorConfig.java +++ b/src/main/java/com/salessparrow/api/lib/errorLib/ErrorConfig.java @@ -4,16 +4,17 @@ @Data public class ErrorConfig { - private String httpCode; - private String code; - private String message; - @Override - public String toString() { - return "ErrorInfo{" + - "http_code='" + httpCode + '\'' + - ", code='" + code + '\'' + - ", message='" + message + '\'' + - '}'; - } + private String httpCode; + + private String code; + + private String message; + + @Override + public String toString() { + return "ErrorInfo{" + "http_code='" + httpCode + '\'' + ", code='" + code + '\'' + ", message='" + message + + '\'' + '}'; + } + } \ No newline at end of file diff --git a/src/main/java/com/salessparrow/api/lib/errorLib/ErrorObject.java b/src/main/java/com/salessparrow/api/lib/errorLib/ErrorObject.java index a6e26c77..d6ae1d83 100644 --- a/src/main/java/com/salessparrow/api/lib/errorLib/ErrorObject.java +++ b/src/main/java/com/salessparrow/api/lib/errorLib/ErrorObject.java @@ -5,17 +5,19 @@ @Data public class ErrorObject { - private String internalErrorIdentifier; - private String apiErrorIdentifier; - private String message; - - public ErrorObject() { - } - - public ErrorObject(String internalErrorIdentifier, String apiErrorIdentifier, String message) { - this.internalErrorIdentifier = internalErrorIdentifier; - this.apiErrorIdentifier = apiErrorIdentifier; - this.message = message; - } + private String internalErrorIdentifier; + + private String apiErrorIdentifier; + + private String message; + + public ErrorObject() { + } + + public ErrorObject(String internalErrorIdentifier, String apiErrorIdentifier, String message) { + this.internalErrorIdentifier = internalErrorIdentifier; + this.apiErrorIdentifier = apiErrorIdentifier; + this.message = message; + } } diff --git a/src/main/java/com/salessparrow/api/lib/errorLib/ErrorResponseObject.java b/src/main/java/com/salessparrow/api/lib/errorLib/ErrorResponseObject.java index c0a9ce35..4fb6e8ed 100644 --- a/src/main/java/com/salessparrow/api/lib/errorLib/ErrorResponseObject.java +++ b/src/main/java/com/salessparrow/api/lib/errorLib/ErrorResponseObject.java @@ -10,27 +10,33 @@ @Data @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) public class ErrorResponseObject { - int httpCode; - String message; - String code; - String internalErrorIdentifier; - List paramErrors; - - public ErrorResponseObject() { - } - - public ErrorResponseObject(int httpCode, String message, String code, String internalErrorIdentifier, - List paramErrors) { - this.httpCode = httpCode; - this.message = message; - this.code = code; - this.internalErrorIdentifier = internalErrorIdentifier; - this.paramErrors = paramErrors; - } - - @Override - public String toString() { - return "ErrorResponseObject [code=" + code + ", paramErrors=" + paramErrors + ", httpCode=" + httpCode - + ", internalErrorIdentifier=" + internalErrorIdentifier + ", message=" + message + "]"; - } + + int httpCode; + + String message; + + String code; + + String internalErrorIdentifier; + + List paramErrors; + + public ErrorResponseObject() { + } + + public ErrorResponseObject(int httpCode, String message, String code, String internalErrorIdentifier, + List paramErrors) { + this.httpCode = httpCode; + this.message = message; + this.code = code; + this.internalErrorIdentifier = internalErrorIdentifier; + this.paramErrors = paramErrors; + } + + @Override + public String toString() { + return "ErrorResponseObject [code=" + code + ", paramErrors=" + paramErrors + ", httpCode=" + httpCode + + ", internalErrorIdentifier=" + internalErrorIdentifier + ", message=" + message + "]"; + } + } diff --git a/src/main/java/com/salessparrow/api/lib/errorLib/ParamErrorConfig.java b/src/main/java/com/salessparrow/api/lib/errorLib/ParamErrorConfig.java index 31ce85f5..92587a72 100644 --- a/src/main/java/com/salessparrow/api/lib/errorLib/ParamErrorConfig.java +++ b/src/main/java/com/salessparrow/api/lib/errorLib/ParamErrorConfig.java @@ -8,25 +8,26 @@ @Data @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) public class ParamErrorConfig { - private String parameter; - private String paramErrorIdentifier; - private String message; - - public ParamErrorConfig() { - } - - public ParamErrorConfig(String parameter, String paramErrorIdentifier, String message) { - this.parameter = parameter; - this.paramErrorIdentifier = paramErrorIdentifier; - this.message = message; - } - - @Override - public String toString() { - return "{" + - "parameter='" + parameter + '\'' + - ", paramErrorIdentifier='" + paramErrorIdentifier + '\'' + - ", message='" + message + '\'' + - '}'; - } + + private String parameter; + + private String paramErrorIdentifier; + + private String message; + + public ParamErrorConfig() { + } + + public ParamErrorConfig(String parameter, String paramErrorIdentifier, String message) { + this.parameter = parameter; + this.paramErrorIdentifier = paramErrorIdentifier; + this.message = message; + } + + @Override + public String toString() { + return "{" + "parameter='" + parameter + '\'' + ", paramErrorIdentifier='" + paramErrorIdentifier + '\'' + + ", message='" + message + '\'' + '}'; + } + } diff --git a/src/main/java/com/salessparrow/api/lib/errorLib/ParamErrorObject.java b/src/main/java/com/salessparrow/api/lib/errorLib/ParamErrorObject.java index 758f2b40..3ca7457c 100644 --- a/src/main/java/com/salessparrow/api/lib/errorLib/ParamErrorObject.java +++ b/src/main/java/com/salessparrow/api/lib/errorLib/ParamErrorObject.java @@ -6,17 +6,20 @@ @Data public class ParamErrorObject { - - private String internalErrorIdentifier; - private String message; - private List paramErrorIdentifiers; - - public ParamErrorObject() { - } - - public ParamErrorObject(String internalErrorIdentifier, String message, List paramErrorIdentifiers) { - this.internalErrorIdentifier = internalErrorIdentifier; - this.message = message; - this.paramErrorIdentifiers = paramErrorIdentifiers; - } + + private String internalErrorIdentifier; + + private String message; + + private List paramErrorIdentifiers; + + public ParamErrorObject() { + } + + public ParamErrorObject(String internalErrorIdentifier, String message, List paramErrorIdentifiers) { + this.internalErrorIdentifier = internalErrorIdentifier; + this.message = message; + this.paramErrorIdentifiers = paramErrorIdentifiers; + } + } diff --git a/src/main/java/com/salessparrow/api/lib/globalConstants/AccountConstants.java b/src/main/java/com/salessparrow/api/lib/globalConstants/AccountConstants.java new file mode 100644 index 00000000..e97424f2 --- /dev/null +++ b/src/main/java/com/salessparrow/api/lib/globalConstants/AccountConstants.java @@ -0,0 +1,11 @@ +package com.salessparrow.api.lib.globalConstants; + +public class AccountConstants { + + public static final String BASIC_VIEW_KIND = "BASIC"; + + public static final String FEED_VIEW_KIND = "FEED"; + + public static final int PAGINATION_LIMIT = 10; + +} diff --git a/src/main/java/com/salessparrow/api/lib/globalConstants/ApiSource.java b/src/main/java/com/salessparrow/api/lib/globalConstants/ApiSource.java index ce517936..41ebb34f 100644 --- a/src/main/java/com/salessparrow/api/lib/globalConstants/ApiSource.java +++ b/src/main/java/com/salessparrow/api/lib/globalConstants/ApiSource.java @@ -5,5 +5,6 @@ @Component public class ApiSource { - public static final String APP = "app"; + public static final String APP = "app"; + } diff --git a/src/main/java/com/salessparrow/api/lib/globalConstants/CacheConstants.java b/src/main/java/com/salessparrow/api/lib/globalConstants/CacheConstants.java index 42d56a54..c2863d9e 100644 --- a/src/main/java/com/salessparrow/api/lib/globalConstants/CacheConstants.java +++ b/src/main/java/com/salessparrow/api/lib/globalConstants/CacheConstants.java @@ -5,11 +5,16 @@ @Component public class CacheConstants { - public static final String SalesSparrowPrefix = "ss_"; + public static final String SalesSparrowPrefix = "ss_"; - public static final String SS_SALESFORCE_USER_CACHE = SalesSparrowPrefix + "sf_user"; - public static final Integer SS_SALESFORCE_USER_CACHE_EXP = 30 * 24 * 60 * 60; // 30 days + public static final String SS_SALESFORCE_USER_CACHE = SalesSparrowPrefix + "sf_user"; + + public static final Integer SS_SALESFORCE_USER_CACHE_EXP = 30 * 24 * 60 * 60; // 30 + // days + + public static final String SS_SALESFORCE_OAUTH_TOKEN_CACHE = SalesSparrowPrefix + "sf_oauth_token"; + + public static final Integer SS_SALESFORCE_OAUTH_TOKEN_CACHE_EXP = 30 * 24 * 60 * 60; // 30 + // days - public static final String SS_SALESFORCE_OAUTH_TOKEN_CACHE = SalesSparrowPrefix + "sf_oauth_token"; - public static final Integer SS_SALESFORCE_OAUTH_TOKEN_CACHE_EXP = 30 * 24 * 60 * 60; // 30 days } diff --git a/src/main/java/com/salessparrow/api/lib/globalConstants/CookieConstants.java b/src/main/java/com/salessparrow/api/lib/globalConstants/CookieConstants.java index 193e3170..e1d870a2 100644 --- a/src/main/java/com/salessparrow/api/lib/globalConstants/CookieConstants.java +++ b/src/main/java/com/salessparrow/api/lib/globalConstants/CookieConstants.java @@ -5,9 +5,10 @@ @Component public class CookieConstants { - public static final String LATEST_VERSION = "1"; + public static final String LATEST_VERSION = "1"; - public static final String USER_LOGIN_COOKIE_NAME = "ulcn"; + public static final String USER_LOGIN_COOKIE_NAME = "ulcn"; + + public static final Integer USER_LOGIN_COOKIE_EXPIRY_IN_SEC = 30 * 60 * 60 * 24; - public static final Integer USER_LOGIN_COOKIE_EXPIRY_IN_SEC = 30 * 60 * 60 * 24; } diff --git a/src/main/java/com/salessparrow/api/lib/globalConstants/DynamoDbTableNameConstants.java b/src/main/java/com/salessparrow/api/lib/globalConstants/DynamoDbTableNameConstants.java index 3a0512b9..9e0f3774 100644 --- a/src/main/java/com/salessparrow/api/lib/globalConstants/DynamoDbTableNameConstants.java +++ b/src/main/java/com/salessparrow/api/lib/globalConstants/DynamoDbTableNameConstants.java @@ -1,33 +1,34 @@ package com.salessparrow.api.lib.globalConstants; +import com.salessparrow.api.config.CoreConstants; + /** * This class contains the names of the DynamoDB tables used by the application. */ public class DynamoDbTableNameConstants { - private static String environment = System.getenv("ENVIRONMENT"); + /** + * Returns the name of the table that contains the Salesforce organizations. + * @return + */ + public static String salesforceOrganizationsTableName() { + return CoreConstants.environment() + "_salesforce_organizations"; + } - /** - * Returns the name of the table that contains the Salesforce organizations. - * @return - */ - public static String salesforceOrganizationsTableName() { - return environment + "_salesforce_organizations"; - } + /** + * Returns the name of the table that contains the Salesforce OAuth tokens. + * @return + */ + public static String salesforceOauthTokensTableName() { + return CoreConstants.environment() + "_salesforce_oauth_tokens"; + } - /** - * Returns the name of the table that contains the Salesforce OAuth tokens. - * @return - */ - public static String salesforceOauthTokensTableName() { - return environment + "_salesforce_oauth_tokens"; - } + /** + * Returns the name of the table that contains the Salesforce users. + * @return + */ + public static String salesforceUsersTableName() { + return CoreConstants.environment() + "_salesforce_users"; + } - /** - * Returns the name of the table that contains the Salesforce users. - * @return - */ - public static String salesforceUsersTableName() { - return environment + "_salesforce_users"; - } } diff --git a/src/main/java/com/salessparrow/api/lib/globalConstants/OpenAiConstants.java b/src/main/java/com/salessparrow/api/lib/globalConstants/OpenAiConstants.java new file mode 100644 index 00000000..2c2c0035 --- /dev/null +++ b/src/main/java/com/salessparrow/api/lib/globalConstants/OpenAiConstants.java @@ -0,0 +1,16 @@ +package com.salessparrow.api.lib.globalConstants; + +import org.springframework.stereotype.Component; + +@Component +public class OpenAiConstants { + + public String chatCompletionUrl() { + return "https://api.openai.com/v1/chat/completions"; + } + + public Integer timeoutMillis() { + return 20000; + } + +} diff --git a/src/main/java/com/salessparrow/api/lib/globalConstants/SalesforceConstants.java b/src/main/java/com/salessparrow/api/lib/globalConstants/SalesforceConstants.java index d379d8d0..139d5639 100644 --- a/src/main/java/com/salessparrow/api/lib/globalConstants/SalesforceConstants.java +++ b/src/main/java/com/salessparrow/api/lib/globalConstants/SalesforceConstants.java @@ -6,55 +6,72 @@ @Component public class SalesforceConstants { - public String compositeUrlPath() { - return "/services/data/v58.0/composite"; - } + public String compositeUrlPath() { + return "/services/data/v58.0/composite"; + } - public String queryUrlPath() { - return "/services/data/v58.0/query/?q="; - } + public String queryUrlPath() { + return "/services/data/v58.0/query/?q="; + } - public String sObjectsPath() { - return "/services/data/v58.0/sobjects"; - } + public String sObjectsPath() { + return "/services/data/v58.0/sobjects"; + } - public String salesforceCompositeUrl(String urlPrefix) { - return urlPrefix + compositeUrlPath(); - } + public String salesforceCompositeUrl(String urlPrefix) { + return urlPrefix + compositeUrlPath(); + } - public String oauth2AuthorizeUrl() { - return CoreConstants.salesforceAuthUrl() + "/services/oauth2/authorize"; - } + public String oauth2AuthorizeUrl() { + return CoreConstants.salesforceAuthUrl() + "/services/oauth2/authorize"; + } - public String oauth2Url() { - return CoreConstants.salesforceAuthUrl() + "/services/oauth2/token"; - } + public String oauth2Url() { + return CoreConstants.salesforceAuthUrl() + "/services/oauth2/token"; + } - public String salesforceCreateNoteUrl() { - return sObjectsPath() + "/ContentNote"; - } + public String revokeTokensUrl() { + return "/services/oauth2/revoke"; + } - public String salesforceAttachNoteUrl() { - return sObjectsPath() + "/ContentDocumentLink"; - } + public String salesforceCreateNoteUrl() { + return sObjectsPath() + "/ContentNote"; + } - public String identityUrl() { - return "/services/oauth2/userinfo"; - } + public String salesforceDeleteNoteUrl(String noteId) { + return sObjectsPath() + "/ContentNote/" + noteId; + } - public String authorizationCodeGrantType() { - return "authorization_code"; - } + public String salesforceAttachNoteUrl() { + return sObjectsPath() + "/ContentDocumentLink"; + } - public String refreshTokenGrantType() { - return "refresh_token"; - } + public String identityUrl() { + return "/services/oauth2/userinfo"; + } - public Integer timeoutMillis() { - return 10000; - } + public String authorizationCodeGrantType() { + return "authorization_code"; + } + + public String refreshTokenGrantType() { + return "refresh_token"; + } + + public Integer timeoutMillis() { + return 10000; + } + + public String salesforceNotesContentUrl(String urlPrefix, String noteId) { + return urlPrefix + "/services/data/v58.0/sobjects/ContentNote/" + noteId + "/Content"; + } + + public String salesforceCreateTaskUrl() { + return sObjectsPath() + "/Task"; + } + + public String salesforceDeleteAccountTaskUrl(String taskId) { + return sObjectsPath() + "/Task/" + taskId; + } - public String salesforceNotesContentUrl(String urlPrefix, String noteId){ - return urlPrefix + "/services/data/v58.0/sobjects/ContentNote/" + noteId + "/Content"; - } } diff --git a/src/main/java/com/salessparrow/api/lib/globalConstants/SecretConstants.java b/src/main/java/com/salessparrow/api/lib/globalConstants/SecretConstants.java new file mode 100644 index 00000000..89d646b0 --- /dev/null +++ b/src/main/java/com/salessparrow/api/lib/globalConstants/SecretConstants.java @@ -0,0 +1,168 @@ +package com.salessparrow.api.lib.globalConstants; + +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; + +import com.amazonaws.secretsmanager.caching.SecretCache; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.salessparrow.api.config.CoreConstants; +import com.salessparrow.api.exception.CustomException; +import com.salessparrow.api.lib.errorLib.ErrorObject; + +import software.amazon.awssdk.services.secretsmanager.SecretsManagerClient; +import software.amazon.awssdk.services.secretsmanager.SecretsManagerClientBuilder; + +/** + * Class to get the environment variables from aws secret manager. + */ +public class SecretConstants { + + /* Secret manager configuration start */ + + /** + * This is the builder that is going to be used to access the secrets manager. + */ + public static SecretsManagerClientBuilder secretsManagerClientBuilder = SecretsManagerClient.builder(); + + /** + * This method returns the secrets from the secrets manager. + * @return + * @throws JsonProcessingException + * @throws JsonMappingException + */ + public static String getSecret(String key) { + String secretJson = ""; + + if (CoreConstants.isDevEnvironment()) { + secretJson = getLocalEnvVars("secrets.json"); + } + else if (CoreConstants.isLocalTestEnvironment()) { + secretJson = getLocalEnvVars("test.secrets.json"); + } + else { + SecretCache cache = new SecretCache(secretsManagerClientBuilder); + String secretId = getSecretId(); + secretJson = cache.getSecretString(secretId); + cache.close(); + } + + ObjectMapper objectMapper = new ObjectMapper(); + String specificValue = ""; + try { + JsonNode jsonNode = objectMapper.readTree(secretJson); + specificValue = jsonNode.get(key).asText(); + } + catch (Exception e) { + throw new CustomException(new ErrorObject("l_gc_s_gs_1", "something_went_wrong", e.getMessage())); + } + + return specificValue; + } + + /** + * This method returns the secret id that is going to be used to access the secrets + * manager. + * @return + */ + private static String getSecretId() { + return "ai-sales-sparrow-api-" + CoreConstants.environment(); + } + + /* Secret manager configuration function end */ + + /* Secrets start */ + + public static String cookieDomain() { + return getSecret("COOKIE_DOMAIN"); + } + + public static String awsRegion() { + return getSecret("AWS_IAM_REGION"); + } + + public static String encryptionKey() { + return getSecret("ENCRYPTION_KEY"); + } + + public static String apiCookieSecret() { + return getSecret("API_COOKIE_SECRET"); + } + + public static String kmsKeyId() { + return getSecret("KMS_KEY_ID"); + } + + public static String salesforceAuthUrl() { + return getSecret("SALESFORCE_AUTH_URL"); + } + + public static String salesforceClientId() { + return getSecret("SALESFORCE_CLIENT_ID"); + } + + public static String salesforceClientSecret() { + return getSecret("SALESFORCE_CLIENT_SECRET"); + } + + public static String memcachedHost() { + return getSecret("MEMCACHED_CACHE_HOST"); + } + + public static String memcachedPort() { + return getSecret("MEMCACHED_CACHE_PORT"); + } + + public static String salesforceWhitelistedRedirectUris() { + return getSecret("SALESFORCE_WHITELISTED_REDIRECT_URIS"); + } + + public static String openAiApiKey() { + return getSecret("OPENAI_API_KEY"); + } + + public static String errorEmailFrom() { + return getSecret("ERROR_MAIL_FROM"); + } + + public static String errorEmailTo() { + return getSecret("ERROR_MAIL_TO"); + } + + public static String dynamoDbUrl() { + return getSecret("DYNAMO_DB_URL"); + } + + public static String localKmsEndpoint() { + return getSecret("LOCAL_KMS_ENDPOINT"); + } + + /* Secrets end */ + + /** + * This method returns the local environment variables. + * @return String + */ + private static String getLocalEnvVars(String filename) { + try (FileReader fileReader = new FileReader(filename)) { + int ch; + StringBuilder secretJsonBuilder = new StringBuilder(); + + while ((ch = fileReader.read()) != -1) { + secretJsonBuilder.append((char) ch); + } + + return secretJsonBuilder.toString(); + } + catch (FileNotFoundException e) { + throw new CustomException(new ErrorObject("l_gc_s_glev_1", "something_went_wrong", e.getMessage())); + } + catch (IOException e) { + throw new CustomException(new ErrorObject("l_gc_s_glev_2", "something_went_wrong", e.getMessage())); + } + } + +} diff --git a/src/main/java/com/salessparrow/api/lib/globalConstants/UserConstants.java b/src/main/java/com/salessparrow/api/lib/globalConstants/UserConstants.java index 773e26f4..43fb1c8a 100644 --- a/src/main/java/com/salessparrow/api/lib/globalConstants/UserConstants.java +++ b/src/main/java/com/salessparrow/api/lib/globalConstants/UserConstants.java @@ -4,5 +4,12 @@ * User constants. */ public class UserConstants { - public static final String SALESFORCE_USER_KIND = "SALESFORCE"; + + public static final String SALESFORCE_USER_KIND = "SALESFORCE"; + + // Private constructor to prevent instantiation + private UserConstants() { + throw new UnsupportedOperationException("This is a utility class and cannot be instantiated"); + } + } diff --git a/src/main/java/com/salessparrow/api/lib/globalConstants/config/dynamoBeeConfigConstants.java b/src/main/java/com/salessparrow/api/lib/globalConstants/config/dynamoBeeConfigConstants.java index 33dd75b9..99ca5863 100644 --- a/src/main/java/com/salessparrow/api/lib/globalConstants/config/dynamoBeeConfigConstants.java +++ b/src/main/java/com/salessparrow/api/lib/globalConstants/config/dynamoBeeConfigConstants.java @@ -6,15 +6,16 @@ @Component public class dynamoBeeConfigConstants { - public String getChangeLogScanPackage() { - return "com.salessparrow.api.changelogs"; - } + public String getChangeLogScanPackage() { + return "com.salessparrow.api.changelogs"; + } - public String getChangelogTableName() { - return CoreConstants.environment() + "_changelog"; - } + public String getChangelogTableName() { + return CoreConstants.environment() + "_changelog"; + } + + public String authorName() { + return CoreConstants.environment() + "_salessparrow_api"; + } - public String authorName() { - return CoreConstants.environment() + "_salessparrow_api"; - } } diff --git a/src/main/java/com/salessparrow/api/lib/httpLib/HttpClient.java b/src/main/java/com/salessparrow/api/lib/httpLib/HttpClient.java index dfc00415..948810b8 100644 --- a/src/main/java/com/salessparrow/api/lib/httpLib/HttpClient.java +++ b/src/main/java/com/salessparrow/api/lib/httpLib/HttpClient.java @@ -13,97 +13,102 @@ public class HttpClient { - public static class HttpResponse { - private int statusCode; - private String responseBody; - private Map> headers; - private String contentType; + public static class HttpResponse { - public HttpResponse(int statusCode, String responseBody, Map> headers, String contentType) { - this.statusCode = statusCode; - this.responseBody = responseBody; - this.headers = headers; - this.contentType = contentType; - } + private int statusCode; - public HttpResponse() { - } + private String responseBody; - public int getStatusCode() { - return statusCode; - } + private Map> headers; - public String getResponseBody() { - return responseBody; - } + private String contentType; - public Map> getHeaders() { - return headers; - } + public HttpResponse(int statusCode, String responseBody, Map> headers, + String contentType) { + this.statusCode = statusCode; + this.responseBody = responseBody; + this.headers = headers; + this.contentType = contentType; + } - public String getContentType() { - return contentType; - } + public HttpResponse() { + } - public void setResponseBody(String responseBody) { - this.responseBody = responseBody; - } + public int getStatusCode() { + return statusCode; + } - } + public String getResponseBody() { + return responseBody; + } - public static HttpResponse makeGetRequest(String url, Map headers, int timeoutMillis) { + public Map> getHeaders() { + return headers; + } - WebClient webClient = WebClient.builder().build(); + public String getContentType() { + return contentType; + } - WebClient.RequestHeadersSpec request = webClient.get() - .uri(url); + public void setResponseBody(String responseBody) { + this.responseBody = responseBody; + } - if (headers != null) { - request.headers(httpHeaders -> { - headers.forEach(httpHeaders::set); - }); - } + } - Mono> responseMono = request - .retrieve() - .toEntity(String.class); + public static HttpResponse makeGetRequest(String url, Map headers, int timeoutMillis) { - ResponseEntity responseEntity = responseMono.block(Duration.ofMillis(timeoutMillis)); + WebClient webClient = WebClient.builder().build(); - int statusCode = responseEntity.getStatusCode().value(); - String responseBody = responseEntity.getBody(); - Map> responseHeaders = new HashMap<>(responseEntity.getHeaders()); - String contentType = responseEntity.getHeaders().getContentType().toString(); + WebClient.RequestHeadersSpec request = webClient.get().uri(url); - return new HttpResponse(statusCode, responseBody, responseHeaders, contentType); - } + if (headers != null) { + request.headers(httpHeaders -> { + headers.forEach(httpHeaders::set); + }); + } - public static HttpResponse makePostRequest(String url, Map headers, Object requestBody, - int timeoutMillis) { - WebClient webClient = WebClient.builder().build(); + Mono> responseMono = request.retrieve().toEntity(String.class); - WebClient.RequestHeadersSpec request = webClient.post() - .uri(url) - .contentType(MediaType.APPLICATION_JSON) - .bodyValue(requestBody); + ResponseEntity responseEntity = responseMono.block(Duration.ofMillis(timeoutMillis)); - if (headers != null) { - request.headers(httpHeaders -> { - headers.forEach(httpHeaders::set); - }); - } + int statusCode = responseEntity.getStatusCode().value(); + String responseBody = responseEntity.getBody(); + Map> responseHeaders = new HashMap<>(responseEntity.getHeaders()); + String contentType = ""; + if (responseEntity.getHeaders().getContentType() != null) { + contentType = responseEntity.getHeaders().getContentType().toString(); + } + return new HttpResponse(statusCode, responseBody, responseHeaders, contentType); + } - Mono> responseMono = request - .retrieve() - .toEntity(String.class); + public static HttpResponse makePostRequest(String url, Map headers, Object requestBody, + int timeoutMillis) { + WebClient webClient = WebClient.builder().build(); - ResponseEntity responseEntity = responseMono.block(Duration.ofMillis(timeoutMillis)); + WebClient.RequestHeadersSpec request = webClient.post() + .uri(url) + .contentType(MediaType.APPLICATION_JSON) + .bodyValue(requestBody); - int statusCode = responseEntity.getStatusCode().value(); - String responseBody = responseEntity.getBody(); - Map> responseHeaders = new HashMap<>(responseEntity.getHeaders()); - String contentType = responseEntity.getHeaders().getContentType().toString(); + if (headers != null) { + request.headers(httpHeaders -> { + headers.forEach(httpHeaders::set); + }); + } - return new HttpResponse(statusCode, responseBody, responseHeaders, contentType); - } -} + Mono> responseMono = request.retrieve().toEntity(String.class); + + ResponseEntity responseEntity = responseMono.block(Duration.ofMillis(timeoutMillis)); + + int statusCode = responseEntity.getStatusCode().value(); + String responseBody = responseEntity.getBody(); + Map> responseHeaders = new HashMap<>(responseEntity.getHeaders()); + String contentType = ""; + if (responseEntity.getHeaders().getContentType() != null) { + contentType = responseEntity.getHeaders().getContentType().toString(); + } + return new HttpResponse(statusCode, responseBody, responseHeaders, contentType); + } + +} \ No newline at end of file diff --git a/src/main/java/com/salessparrow/api/lib/openAi/OpenAiPayloadBuilder.java b/src/main/java/com/salessparrow/api/lib/openAi/OpenAiPayloadBuilder.java new file mode 100644 index 00000000..9fc56960 --- /dev/null +++ b/src/main/java/com/salessparrow/api/lib/openAi/OpenAiPayloadBuilder.java @@ -0,0 +1,51 @@ +package com.salessparrow.api.lib.openAi; + +import java.text.SimpleDateFormat; +import java.util.Date; + +import org.springframework.stereotype.Component; + +/** + * OpenAiPayloadBuilder is a class for the payload builder for the open ai. + */ +@Component +public class OpenAiPayloadBuilder { + + /** + * Payload for crm actions suggestions. + * @param text + * @return + */ + public String payloadForCrmActionsSuggestions(String text) { + + return "{\n" + " \"model\": \"gpt-3.5-turbo-0613\",\n" + " \"messages\": [\n" + " {\n" + + " \"role\": \"user\",\n" + + " \"content\": \"You are an AI assistant which gives suggestion on creating task in crm using the input message.Only use the functions you have been provided with. \\nInput message: \\n" + + text + "\\n\"\n" + " }\n" + " ],\n" + " \"functions\": [\n" + " {\n" + + " \"name\": \"suggest_actions\",\n" + + " \"description\": \"This is function for suggesting actions in crm(example salesforce, freshsales) based on input message.\",\n" + + " \"parameters\": {\n" + " \"type\": \"object\",\n" + " \"properties\": {\n" + + " \"add_task\": {\n" + " \"name\": \"add_task\",\n" + + " \"description\": \"Tasks using input message.\",\n" + + " \"type\": \"array\",\n" + " \"items\": {\n" + + " \"type\": \"object\",\n" + " \"properties\": {\n" + + " \"description\": {\n" + " \"type\": \"string\",\n" + + " \"description\": \"Description for task to add. This is mandatory\"\n" + + " },\n" + " \"due_date\": {\n" + + " \"type\": \"string\",\n" + + " \"description\": \"Due date for task in YYYY-MM-DD format. Today's date is " + + getTodaysDate() + ". This is mandatory\"\n" + " }\n" + " },\n" + + " \"required\": [\"description\", \"due_date\"]\n" + " }\n" + " }\n" + + " }\n" + " }\n" + " }\n" + " ]\n" + "}"; + } + + /** + * Todays date in yyyy-MM-dd format. + * @return + */ + public String getTodaysDate() { + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); + return sdf.format(new Date()); + } + +} diff --git a/src/main/java/com/salessparrow/api/lib/openAi/OpenAiRequest.java b/src/main/java/com/salessparrow/api/lib/openAi/OpenAiRequest.java new file mode 100644 index 00000000..c9389108 --- /dev/null +++ b/src/main/java/com/salessparrow/api/lib/openAi/OpenAiRequest.java @@ -0,0 +1,62 @@ +package com.salessparrow.api.lib.openAi; + +import java.util.HashMap; +import java.util.Map; + +import org.slf4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.client.WebClientResponseException; + +import com.salessparrow.api.config.CoreConstants; +import com.salessparrow.api.controllers.SuggestionsController; +import com.salessparrow.api.exception.CustomException; +import com.salessparrow.api.lib.errorLib.ErrorObject; +import com.salessparrow.api.lib.globalConstants.OpenAiConstants; +import com.salessparrow.api.lib.httpLib.HttpClient; + +/** + * OpenAiRequest is a class for making a request to the OpenAI API. + **/ +@Component +public class OpenAiRequest { + + @Autowired + private OpenAiConstants openAiConstants; + + private Logger logger = org.slf4j.LoggerFactory.getLogger(SuggestionsController.class); + + /** + * Make a request to the OpenAI API. + * @param payload + * @return + */ + public HttpClient.HttpResponse makeRequest(Object payload) { + String httpReqUrl = openAiConstants.chatCompletionUrl(); + + Map headers = new HashMap<>(); + headers.put("Authorization", "Bearer " + CoreConstants.openAiApiKey()); + + Integer timeoutMillis = openAiConstants.timeoutMillis(); + try { + logger.info("Making request to OpenAI API"); + HttpClient.HttpResponse response = HttpClient.makePostRequest(httpReqUrl, headers, payload, timeoutMillis); + + return response; + } + catch (WebClientResponseException e) { + logger.error("Error while making request to OpenAI API: " + e.getResponseBodyAsString()); + if (e.getStatusCode().value() == 401) { + throw new CustomException( + new ErrorObject("l_o_a_oar_mr_1", "something_went_wrong", "Invalid OpenAI API key")); + } + else if (e.getStatusCode().value() == 400) { + throw new CustomException( + new ErrorObject("l_o_a_oar_mr_2", "something_went_wrong", "Invalid request payload")); + } + + throw new CustomException(new ErrorObject("l_o_a_oar_mr_3", "something_went_wrong", e.getMessage())); + } + } + +} diff --git a/src/main/java/com/salessparrow/api/lib/salesforce/dto/CompositeRequestDto.java b/src/main/java/com/salessparrow/api/lib/salesforce/dto/CompositeRequestDto.java index 29d9f93f..f931f093 100644 --- a/src/main/java/com/salessparrow/api/lib/salesforce/dto/CompositeRequestDto.java +++ b/src/main/java/com/salessparrow/api/lib/salesforce/dto/CompositeRequestDto.java @@ -4,22 +4,26 @@ @Data public class CompositeRequestDto { - - private String method; - private String url; - private String referenceId; - private Object body; - - public CompositeRequestDto(String method, String url, String referenceId) { - this.method = method; - this.url = url; - this.referenceId = referenceId; - } - - public CompositeRequestDto(String method, String url, String referenceId, Object body) { - this.method = method; - this.url = url; - this.referenceId = referenceId; - this.body = body; - } + + private String method; + + private String url; + + private String referenceId; + + private Object body; + + public CompositeRequestDto(String method, String url, String referenceId) { + this.method = method; + this.url = url; + this.referenceId = referenceId; + } + + public CompositeRequestDto(String method, String url, String referenceId, Object body) { + this.method = method; + this.url = url; + this.referenceId = referenceId; + this.body = body; + } + } diff --git a/src/main/java/com/salessparrow/api/lib/salesforce/dto/SalesforceAccountDto.java b/src/main/java/com/salessparrow/api/lib/salesforce/dto/SalesforceAccountDto.java index 647e8b73..4a7a6377 100644 --- a/src/main/java/com/salessparrow/api/lib/salesforce/dto/SalesforceAccountDto.java +++ b/src/main/java/com/salessparrow/api/lib/salesforce/dto/SalesforceAccountDto.java @@ -1,7 +1,12 @@ package com.salessparrow.api.lib.salesforce.dto; +import java.util.HashMap; +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonAnySetter; import com.fasterxml.jackson.databind.PropertyNamingStrategies; import com.fasterxml.jackson.databind.annotation.JsonNaming; +import com.google.common.base.CaseFormat; import com.salessparrow.api.dto.entities.AccountEntity; import lombok.Data; @@ -9,14 +14,30 @@ @Data @JsonNaming(PropertyNamingStrategies.UpperCamelCaseStrategy.class) public class SalesforceAccountDto { - private String id; - private String name; - public AccountEntity getAccountEntity() { - AccountEntity accountEntity = new AccountEntity(); - accountEntity.setId(this.id); - accountEntity.setName(this.name); + private String id; + + private String name; + + private Map additionalFields = new HashMap<>(); + + private SalesforceContactWrapperDto Contacts; + + public AccountEntity getAccountEntity() { + AccountEntity accountEntity = new AccountEntity(); + accountEntity.setId(this.id); + accountEntity.setName(this.name); + accountEntity.setAdditionalFields(this.additionalFields); + accountEntity.setAccountContactAssociationsId(this.id); + return accountEntity; + } + + @JsonAnySetter + public void setAdditionalField(String fieldName, Object value) { + fieldName = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, fieldName); + if (!fieldName.equals("attributes") && !fieldName.equals("Contacts")) { + additionalFields.put(fieldName, value); + } + } - return accountEntity; - } } diff --git a/src/main/java/com/salessparrow/api/lib/salesforce/dto/SalesforceContactDto.java b/src/main/java/com/salessparrow/api/lib/salesforce/dto/SalesforceContactDto.java new file mode 100644 index 00000000..f6411932 --- /dev/null +++ b/src/main/java/com/salessparrow/api/lib/salesforce/dto/SalesforceContactDto.java @@ -0,0 +1,40 @@ +package com.salessparrow.api.lib.salesforce.dto; + +import java.util.HashMap; +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import com.google.common.base.CaseFormat; +import com.salessparrow.api.dto.entities.ContactEntity; + +import lombok.Data; + +@Data +@JsonNaming(PropertyNamingStrategies.UpperCamelCaseStrategy.class) +public class SalesforceContactDto { + + private String id; + + private String name; + + private Map additionalFields = new HashMap<>(); + + public ContactEntity getContactEntity() { + ContactEntity contactEntity = new ContactEntity(); + contactEntity.setId(this.id); + contactEntity.setName(this.name); + contactEntity.setAdditionalFields(this.additionalFields); + return contactEntity; + } + + @JsonAnySetter + public void setAdditionalField(String fieldName, Object value) { + fieldName = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, fieldName); + if (!fieldName.equals("attributes")) { + additionalFields.put(fieldName, value); + } + } + +} \ No newline at end of file diff --git a/src/main/java/com/salessparrow/api/lib/salesforce/dto/SalesforceContactWrapperDto.java b/src/main/java/com/salessparrow/api/lib/salesforce/dto/SalesforceContactWrapperDto.java new file mode 100644 index 00000000..d4042cd0 --- /dev/null +++ b/src/main/java/com/salessparrow/api/lib/salesforce/dto/SalesforceContactWrapperDto.java @@ -0,0 +1,13 @@ +package com.salessparrow.api.lib.salesforce.dto; + +import java.util.ArrayList; +import java.util.List; + +import lombok.Data; + +@Data +public class SalesforceContactWrapperDto { + + private List records = new ArrayList<>(); + +} \ No newline at end of file diff --git a/src/main/java/com/salessparrow/api/lib/salesforce/dto/SalesforceCreateNoteDto.java b/src/main/java/com/salessparrow/api/lib/salesforce/dto/SalesforceCreateNoteDto.java index f9c4445f..cee8a169 100644 --- a/src/main/java/com/salessparrow/api/lib/salesforce/dto/SalesforceCreateNoteDto.java +++ b/src/main/java/com/salessparrow/api/lib/salesforce/dto/SalesforceCreateNoteDto.java @@ -7,9 +7,11 @@ */ @Data public class SalesforceCreateNoteDto { - String id; - String success; - - String[] errors; + String id; + + String success; + + String[] errors; + } diff --git a/src/main/java/com/salessparrow/api/lib/salesforce/dto/SalesforceCreateTaskDto.java b/src/main/java/com/salessparrow/api/lib/salesforce/dto/SalesforceCreateTaskDto.java new file mode 100644 index 00000000..a8053476 --- /dev/null +++ b/src/main/java/com/salessparrow/api/lib/salesforce/dto/SalesforceCreateTaskDto.java @@ -0,0 +1,10 @@ +package com.salessparrow.api.lib.salesforce.dto; + +import lombok.Data; + +@Data +public class SalesforceCreateTaskDto { + + String id; + +} diff --git a/src/main/java/com/salessparrow/api/lib/salesforce/dto/SalesforceCrmOrganizationUserDto.java b/src/main/java/com/salessparrow/api/lib/salesforce/dto/SalesforceCrmOrganizationUserDto.java new file mode 100644 index 00000000..bb3b69ab --- /dev/null +++ b/src/main/java/com/salessparrow/api/lib/salesforce/dto/SalesforceCrmOrganizationUserDto.java @@ -0,0 +1,28 @@ +package com.salessparrow.api.lib.salesforce.dto; + +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import com.salessparrow.api.dto.entities.CrmOrganizationUserEntity; + +import lombok.Data; + +@Data +@JsonNaming(PropertyNamingStrategies.UpperCamelCaseStrategy.class) +public class SalesforceCrmOrganizationUserDto { + + private String id; + + private String name; + + /** + * convert SalesforceCrmOrganizationUserDto into CrmOrganizationUserEntity + */ + public CrmOrganizationUserEntity getCrmOrganizationUserEntity() { + CrmOrganizationUserEntity crmOrganizationUserEntity = new CrmOrganizationUserEntity(); + crmOrganizationUserEntity.setId(this.id); + crmOrganizationUserEntity.setName(this.name); + + return crmOrganizationUserEntity; + } + +} diff --git a/src/main/java/com/salessparrow/api/lib/salesforce/dto/SalesforceGetIdentityDto.java b/src/main/java/com/salessparrow/api/lib/salesforce/dto/SalesforceGetIdentityDto.java index 84a00108..87d007c7 100644 --- a/src/main/java/com/salessparrow/api/lib/salesforce/dto/SalesforceGetIdentityDto.java +++ b/src/main/java/com/salessparrow/api/lib/salesforce/dto/SalesforceGetIdentityDto.java @@ -8,9 +8,15 @@ @Data @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) public class SalesforceGetIdentityDto { - private String sub; - private String userId; - private String organizationId; - private String name; - private String email; + + private String sub; + + private String userId; + + private String organizationId; + + private String name; + + private String email; + } diff --git a/src/main/java/com/salessparrow/api/lib/salesforce/dto/SalesforceGetNoteDetailsDto.java b/src/main/java/com/salessparrow/api/lib/salesforce/dto/SalesforceGetNoteDetailsDto.java index ae5bdc4b..bb8422d2 100644 --- a/src/main/java/com/salessparrow/api/lib/salesforce/dto/SalesforceGetNoteDetailsDto.java +++ b/src/main/java/com/salessparrow/api/lib/salesforce/dto/SalesforceGetNoteDetailsDto.java @@ -11,23 +11,25 @@ @Data @JsonNaming(PropertyNamingStrategies.UpperCamelCaseStrategy.class) public class SalesforceGetNoteDetailsDto { - private String id; - private CreatedBy createdBy; - private Date lastModifiedDate; - - @Data - @JsonNaming(PropertyNamingStrategies.UpperCamelCaseStrategy.class) - private class CreatedBy{ - private String name; - } - - public NoteDetailEntity noteDetailEntity(String noteContentResponse){ - NoteDetailEntity noteDetailEntity = new NoteDetailEntity( - this.id, - this.createdBy.name, - noteContentResponse, - this.lastModifiedDate - ); - return noteDetailEntity; - } + + private String id; + + private CreatedBy createdBy; + + private Date lastModifiedDate; + + @Data + @JsonNaming(PropertyNamingStrategies.UpperCamelCaseStrategy.class) + private class CreatedBy { + + private String name; + + } + + public NoteDetailEntity noteDetailEntity(String noteContentResponse) { + NoteDetailEntity noteDetailEntity = new NoteDetailEntity(this.id, this.createdBy.name, noteContentResponse, + this.lastModifiedDate); + return noteDetailEntity; + } + } diff --git a/src/main/java/com/salessparrow/api/lib/salesforce/dto/SalesforceGetNoteIdDto.java b/src/main/java/com/salessparrow/api/lib/salesforce/dto/SalesforceGetNoteIdDto.java index d81d79d3..8666b20e 100644 --- a/src/main/java/com/salessparrow/api/lib/salesforce/dto/SalesforceGetNoteIdDto.java +++ b/src/main/java/com/salessparrow/api/lib/salesforce/dto/SalesforceGetNoteIdDto.java @@ -8,5 +8,7 @@ @Data @JsonNaming(PropertyNamingStrategies.UpperCamelCaseStrategy.class) public class SalesforceGetNoteIdDto { - private String contentDocumentId; + + private String contentDocumentId; + } diff --git a/src/main/java/com/salessparrow/api/lib/salesforce/dto/SalesforceGetNotesListDto.java b/src/main/java/com/salessparrow/api/lib/salesforce/dto/SalesforceGetNotesListDto.java index 736562a1..6dab2276 100644 --- a/src/main/java/com/salessparrow/api/lib/salesforce/dto/SalesforceGetNotesListDto.java +++ b/src/main/java/com/salessparrow/api/lib/salesforce/dto/SalesforceGetNotesListDto.java @@ -11,24 +11,26 @@ @Data @JsonNaming(PropertyNamingStrategies.UpperCamelCaseStrategy.class) public class SalesforceGetNotesListDto { - private String id; - private String textPreview; - private CreatedBy createdBy; - private Date lastModifiedDate; - - @Data - @JsonNaming(PropertyNamingStrategies.UpperCamelCaseStrategy.class) - private class CreatedBy{ - private String name; - } - - public NoteEntity noteEntity(){ - NoteEntity noteEntity = new NoteEntity( - this.id, - this.createdBy.name, - this.textPreview, - this.lastModifiedDate - ); - return noteEntity; - } + + private String id; + + private String textPreview; + + private CreatedBy createdBy; + + private Date lastModifiedDate; + + @Data + @JsonNaming(PropertyNamingStrategies.UpperCamelCaseStrategy.class) + private class CreatedBy { + + private String name; + + } + + public NoteEntity noteEntity() { + NoteEntity noteEntity = new NoteEntity(this.id, this.createdBy.name, this.textPreview, this.lastModifiedDate); + return noteEntity; + } + } diff --git a/src/main/java/com/salessparrow/api/lib/salesforce/dto/SalesforceGetTasksListDto.java b/src/main/java/com/salessparrow/api/lib/salesforce/dto/SalesforceGetTasksListDto.java new file mode 100644 index 00000000..f8bfe670 --- /dev/null +++ b/src/main/java/com/salessparrow/api/lib/salesforce/dto/SalesforceGetTasksListDto.java @@ -0,0 +1,60 @@ +package com.salessparrow.api.lib.salesforce.dto; + +import java.util.Date; + +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import com.salessparrow.api.dto.entities.TaskEntity; +import com.salessparrow.api.lib.Util; + +import lombok.Data; + +@Data +@JsonNaming(PropertyNamingStrategies.UpperCamelCaseStrategy.class) +public class SalesforceGetTasksListDto { + + private String id; + + private String description; + + private Date activityDate; + + private CreatedBy createdBy; + + private Owner owner; + + private Date lastModifiedDate; + + @Data + @JsonNaming(PropertyNamingStrategies.UpperCamelCaseStrategy.class) + private class CreatedBy { + + private String name; + + } + + @Data + @JsonNaming(PropertyNamingStrategies.UpperCamelCaseStrategy.class) + private class Owner { + + private String name; + + } + + public TaskEntity taskEntity() { + Util util = new Util(); + + TaskEntity taskEntity = new TaskEntity(); + taskEntity.setId(this.id); + taskEntity.setCreatorName(this.createdBy.name); + taskEntity.setDescription(this.description); + taskEntity.setCrmOrganizationUserName(this.owner.name); + taskEntity.setLastModifiedTime(this.lastModifiedDate); + + String dueDate = util.getDateFormatFromDatetime(this.activityDate); + + taskEntity.setDueDate(dueDate); + return taskEntity; + } + +} diff --git a/src/main/java/com/salessparrow/api/lib/salesforce/dto/SalesforceGetTokensDto.java b/src/main/java/com/salessparrow/api/lib/salesforce/dto/SalesforceGetTokensDto.java index 40365b23..3848b8cd 100644 --- a/src/main/java/com/salessparrow/api/lib/salesforce/dto/SalesforceGetTokensDto.java +++ b/src/main/java/com/salessparrow/api/lib/salesforce/dto/SalesforceGetTokensDto.java @@ -8,33 +8,41 @@ @Data @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) public class SalesforceGetTokensDto { - private String accessToken; - private String refreshToken; - private String signature; - private String scope; - private String idToken; - private String instanceUrl; - private String id; - private String tokenType; - private String issuedAt; - - /** - * Get Salesforce organization id from id - * - * @return String - */ - public String getSalesforceOrganizationId() { - String[] idParts = id.split("/"); - return idParts[4]; - } - - /** - * Get Salesforce user id from id - * - * @return String - */ - public String getSalesforceUserId() { - String[] idParts = id.split("/"); - return idParts[5]; - } + + private String accessToken; + + private String refreshToken; + + private String signature; + + private String scope; + + private String idToken; + + private String instanceUrl; + + private String id; + + private String tokenType; + + private String issuedAt; + + /** + * Get Salesforce organization id from id + * @return String + */ + public String getSalesforceOrganizationId() { + String[] idParts = id.split("/"); + return idParts[4]; + } + + /** + * Get Salesforce user id from id + * @return String + */ + public String getSalesforceUserId() { + String[] idParts = id.split("/"); + return idParts[5]; + } + } diff --git a/src/main/java/com/salessparrow/api/lib/salesforce/helper/MakeCompositeRequest.java b/src/main/java/com/salessparrow/api/lib/salesforce/helper/MakeCompositeRequest.java index 5e28ec31..ac3ebe49 100644 --- a/src/main/java/com/salessparrow/api/lib/salesforce/helper/MakeCompositeRequest.java +++ b/src/main/java/com/salessparrow/api/lib/salesforce/helper/MakeCompositeRequest.java @@ -4,6 +4,8 @@ import java.util.List; import java.util.Map; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -17,45 +19,44 @@ @Component public class MakeCompositeRequest { - @Autowired - private SalesforceRequest salesforceOauthRequest; - - @Autowired - private SalesforceConstants salesforceConstants; - - /** - * Make composite post request to the Salesforce API. - * - * @param compositeRequests - * @param salesforceUserId - * - * @return HttpClient.HttpResponse - **/ - public HttpClient.HttpResponse makePostRequest( - List compositeRequests, - String salesforceUserId) { - Map> compositeRequestsMap = new HashMap<>(); - compositeRequestsMap.put("compositeRequest", compositeRequests); - - Integer timeoutMillis = salesforceConstants.timeoutMillis(); - - SalesforceRequestInterface request = (token, instanceUrl) -> { - String httpReqUrl = salesforceConstants.salesforceCompositeUrl(instanceUrl); - - Map headers = new HashMap<>(); - headers.put("Authorization", "Bearer " + token); - - HttpClient.HttpResponse response = HttpClient.makePostRequest( - httpReqUrl, - headers, - compositeRequestsMap, - timeoutMillis); - return response; - }; - - HttpClient.HttpResponse response = null; - - response = salesforceOauthRequest.makeRequest(salesforceUserId, request); - return response; - } + Logger logger = LoggerFactory.getLogger(MakeCompositeRequest.class); + + @Autowired + private SalesforceRequest salesforceOauthRequest; + + @Autowired + private SalesforceConstants salesforceConstants; + + /** + * Make composite post request to the Salesforce API. + * @param compositeRequests + * @param salesforceUserId + * @return HttpClient.HttpResponse + **/ + public HttpClient.HttpResponse makePostRequest(List compositeRequests, + String salesforceUserId) { + Map> compositeRequestsMap = new HashMap<>(); + compositeRequestsMap.put("compositeRequest", compositeRequests); + + Integer timeoutMillis = salesforceConstants.timeoutMillis(); + + SalesforceRequestInterface request = (token, instanceUrl) -> { + String httpReqUrl = salesforceConstants.salesforceCompositeUrl(instanceUrl); + + Map headers = new HashMap<>(); + headers.put("Authorization", "Bearer " + token); + + HttpClient.HttpResponse response = HttpClient.makePostRequest(httpReqUrl, headers, compositeRequestsMap, + timeoutMillis); + return response; + }; + + HttpClient.HttpResponse response = null; + + logger.info("making composite request to salesforce"); + + response = salesforceOauthRequest.makeRequest(salesforceUserId, request); + return response; + } + } diff --git a/src/main/java/com/salessparrow/api/lib/salesforce/helper/SalesforceOAuthToken.java b/src/main/java/com/salessparrow/api/lib/salesforce/helper/SalesforceOAuthToken.java index 85dc84b0..5c42c3a7 100644 --- a/src/main/java/com/salessparrow/api/lib/salesforce/helper/SalesforceOAuthToken.java +++ b/src/main/java/com/salessparrow/api/lib/salesforce/helper/SalesforceOAuthToken.java @@ -17,57 +17,55 @@ @Service public class SalesforceOAuthToken { - @Autowired - private AwsKms awsKms; - - @Autowired - private SalesforceOauthTokenRepository sfOauthTokenRepository; - - @Autowired - private SalesforceGetRefreshedAccessToken salesforceGetRefreshedAccessToken; - - @Autowired - private Util util; - - /** - * Fetch the access token from the database and decrypt it. - * - * @param sfOAuthToken - * - * @return String - */ - public String fetchAccessToken(SalesforceOauthToken sfOAuthToken) { - String decryptedAccessToken = awsKms.decryptToken(sfOAuthToken.getAccessToken()); - return decryptedAccessToken; - } - - public String updateAndGetRefreshedAccessToken(SalesforceOauthToken sfOAuthToken) { - String encryptedRefreshToken = sfOAuthToken.getRefreshToken(); - String decryptedRefreshToken = awsKms.decryptToken(encryptedRefreshToken); - - HttpClient.HttpResponse response = salesforceGetRefreshedAccessToken.getRefreshedAccessToken(decryptedRefreshToken); - String decryptedAccessToken = updateAccessTokenInDatabase(response.getResponseBody(), sfOAuthToken); - - return decryptedAccessToken; - } - - /** - * Update the access token in the database and return the decrypted access token. - * - * @param responseBody - * @param sfOAuthToken - * - * @return String - */ - private String updateAccessTokenInDatabase(String responseBody, SalesforceOauthToken sfOAuthToken) { - JsonNode rootNode = util.getJsonNode(responseBody); - String decryptedAccessToken = rootNode.get("access_token").asText(); - - String encryptedAccessToken = awsKms.encryptToken(decryptedAccessToken); - - sfOAuthToken.setAccessToken(encryptedAccessToken); - sfOauthTokenRepository.saveSalesforceOauthToken(sfOAuthToken); - - return decryptedAccessToken; - } + @Autowired + private AwsKms awsKms; + + @Autowired + private SalesforceOauthTokenRepository salesforceOauthTokenRepository; + + @Autowired + private SalesforceGetRefreshedAccessToken salesforceGetRefreshedAccessToken; + + @Autowired + private Util util; + + /** + * Fetch the access token from the database and decrypt it. + * @param sfOAuthToken + * @return String + */ + public String fetchAccessToken(SalesforceOauthToken sfOAuthToken) { + String decryptedAccessToken = awsKms.decryptToken(sfOAuthToken.getAccessToken()); + return decryptedAccessToken; + } + + public String updateAndGetRefreshedAccessToken(SalesforceOauthToken sfOAuthToken) { + String encryptedRefreshToken = sfOAuthToken.getRefreshToken(); + String decryptedRefreshToken = awsKms.decryptToken(encryptedRefreshToken); + + HttpClient.HttpResponse response = salesforceGetRefreshedAccessToken + .getRefreshedAccessToken(decryptedRefreshToken); + String decryptedAccessToken = updateAccessTokenInDatabase(response.getResponseBody(), sfOAuthToken); + + return decryptedAccessToken; + } + + /** + * Update the access token in the database and return the decrypted access token. + * @param responseBody + * @param sfOAuthToken + * @return String + */ + private String updateAccessTokenInDatabase(String responseBody, SalesforceOauthToken sfOAuthToken) { + JsonNode rootNode = util.getJsonNode(responseBody); + String decryptedAccessToken = rootNode.get("access_token").asText(); + + String encryptedAccessToken = awsKms.encryptToken(decryptedAccessToken); + + sfOAuthToken.setAccessToken(encryptedAccessToken); + salesforceOauthTokenRepository.updateSalesforceOauthToken(sfOAuthToken); + + return decryptedAccessToken; + } + } diff --git a/src/main/java/com/salessparrow/api/lib/salesforce/helper/SalesforceQueryBuilder.java b/src/main/java/com/salessparrow/api/lib/salesforce/helper/SalesforceQueryBuilder.java index a4cc80a1..eeacbeda 100644 --- a/src/main/java/com/salessparrow/api/lib/salesforce/helper/SalesforceQueryBuilder.java +++ b/src/main/java/com/salessparrow/api/lib/salesforce/helper/SalesforceQueryBuilder.java @@ -4,59 +4,115 @@ import org.springframework.stereotype.Component; +import com.salessparrow.api.lib.Util; + /** * SalesforceQueries is a class for building the Salesforce queries. */ @Component public class SalesforceQueryBuilder { - - /** - * Get the list of accounts for a given searchTerm - * - * @param searchTerm - * - * @return String - */ - public String getAccountsQuery(String searchTerm) { - if (searchTerm == "") { - return "SELECT Id, Name FROM Account ORDER BY LastModifiedDate DESC LIMIT 20"; - } - return "SELECT Id, Name FROM Account WHERE Name LIKE '%25"+searchTerm+"%25' ORDER BY LastModifiedDate DESC LIMIT 20"; - } - - /** - * Get the list of notes for a given account - * - * @param accountId - * @return String - */ - public String getContentDocumentIdUrl(String accountId) { - return "SELECT ContentDocumentId FROM ContentDocumentLink WHERE LinkedEntityId = '" - + accountId + "'"; - } - - /** - * Get the list of notes for a given account - * - * @param documentIds - * @return String - */ - public String getNoteListIdUrl(List documentIds) { - StringBuilder queryBuilder = new StringBuilder( - "SELECT Id, Title, TextPreview, CreatedBy.Name, LastModifiedDate FROM ContentNote WHERE Id IN ("); - for (int i = 0; i < documentIds.size(); i++) { - if (i > 0) { - queryBuilder.append(", "); - } - queryBuilder.append("'").append(documentIds.get(i)).append("'"); - } - queryBuilder.append(") ORDER BY LastModifiedDate DESC LIMIT 5"); - - return queryBuilder.toString(); - } - - public String getNoteDetailsUrl(String noteId){ - return "SELECT Id, Title, TextPreview, CreatedBy.Name, LastModifiedDate FROM ContentNote WHERE Id = '" + noteId + "'"; - } + + /** + * Get the list of accounts for a given searchTerm + * @param searchTerm + * @return String + */ + public String getAccountsQuery(String searchTerm) { + searchTerm = Util.escapeSpecialChars(searchTerm); + + String query = ""; + if (searchTerm == "") { + query = "SELECT Id, Name FROM Account ORDER BY LastModifiedDate DESC LIMIT 20"; + } + else { + query = "SELECT Id, Name FROM Account WHERE Name LIKE '%" + searchTerm + + "%' ORDER BY LastModifiedDate DESC LIMIT 20"; + } + + return Util.urlEncoder(query); + } + + /** + * Get the accounts feed for a given limit and offset + * @param limit int + * @param offset int + * @return String + */ + public String getAccountFeedQuery(int limit, int offset) { + + return Util.urlEncoder(String.format( + "SELECT Id, Name, Website, (SELECT Id, Name, Title, Email, Phone FROM Contacts) FROM Account ORDER BY LastModifiedDate ASC LIMIT %d OFFSET %d", + limit, offset)); + } + + /** + * Get the list of tasks for a given account + * @param accountId + * @return String + */ + public String getAccountTasksQuery(String accountId) { + accountId = Util.escapeSpecialChars(accountId); + + return Util.urlEncoder( + "SELECT Id, Description, ActivityDate, CreatedBy.Name, Owner.Name, LastModifiedDate FROM Task WHERE WhatId='" + + accountId + "' ORDER BY LastModifiedDate DESC LIMIT 5"); + } + + /** + * Get the list of notes for a given account + * @param accountId + * @return String + */ + public String getContentDocumentIdUrl(String accountId) { + accountId = Util.escapeSpecialChars(accountId); + + return Util + .urlEncoder("SELECT ContentDocumentId FROM ContentDocumentLink WHERE LinkedEntityId = '" + accountId + "'"); + } + + /** + * Get the list of notes for a given account + * @param documentIds + * @return String + */ + public String getNoteListIdUrl(List documentIds) { + StringBuilder queryBuilder = new StringBuilder( + "SELECT Id, Title, TextPreview, CreatedBy.Name, LastModifiedDate FROM ContentNote WHERE Id IN ("); + + for (int i = 0; i < documentIds.size(); i++) { + if (i > 0) { + queryBuilder.append(", "); + } + + String documentId = Util.escapeSpecialChars(documentIds.get(i)); + queryBuilder.append("'").append(documentId).append("'"); + } + queryBuilder.append(") ORDER BY LastModifiedDate DESC LIMIT 5"); + + return Util.urlEncoder(queryBuilder.toString()); + } + + public String getNoteDetailsUrl(String noteId) { + noteId = Util.escapeSpecialChars(noteId); + + return Util + .urlEncoder("SELECT Id, Title, TextPreview, CreatedBy.Name, LastModifiedDate FROM ContentNote WHERE Id = '" + + noteId + "'"); + } + + public String getCrmOrganizationUsersQuery(String searchTerm) { + searchTerm = Util.escapeSpecialChars(searchTerm); + String query = ""; + + if (searchTerm == "") { + query = "SELECT Id, Name FROM User WHERE IsActive = true ORDER BY LastModifiedDate DESC LIMIT 20"; + } + else { + query = "SELECT Id, Name FROM User WHERE Name LIKE '%" + searchTerm + + "%' AND IsActive = true ORDER BY LastModifiedDate DESC LIMIT 20"; + } + + return Util.urlEncoder(query); + } } diff --git a/src/main/java/com/salessparrow/api/lib/salesforce/helper/SalesforceRequest.java b/src/main/java/com/salessparrow/api/lib/salesforce/helper/SalesforceRequest.java index e863cf38..3f892dbb 100644 --- a/src/main/java/com/salessparrow/api/lib/salesforce/helper/SalesforceRequest.java +++ b/src/main/java/com/salessparrow/api/lib/salesforce/helper/SalesforceRequest.java @@ -7,54 +7,55 @@ import com.salessparrow.api.domain.SalesforceOauthToken; import com.salessparrow.api.exception.CustomException; import com.salessparrow.api.lib.errorLib.ErrorObject; +import com.salessparrow.api.lib.httpLib.HttpClient; import com.salessparrow.api.repositories.SalesforceOauthTokenRepository; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * SalesforceRequest is a class for making a request to the Salesforce API. */ @Component public class SalesforceRequest { - @Autowired - private SalesforceOAuthToken getAccessTokenService; - - @Autowired - private SalesforceOauthTokenRepository sfOauthTokenRepository; - - /** - * Make a request to the Salesforce API. - * - * @param salesforceUserId - * @param request - * - * @return T - */ - public T makeRequest(String salesforceUserId, SalesforceRequestInterface request) { - SalesforceOauthToken sfOAuthToken = sfOauthTokenRepository - .getSalesforceOauthTokenByExternalUserId(salesforceUserId); - - String decryptedAccessToken = getAccessTokenService.fetchAccessToken(sfOAuthToken); - - try { - return request.execute(decryptedAccessToken, sfOAuthToken.getInstanceUrl()); - } catch (WebClientResponseException e) { - if(e.getStatusCode().value() == 401) { - try { - decryptedAccessToken = getAccessTokenService.updateAndGetRefreshedAccessToken(sfOAuthToken); - return request.execute(decryptedAccessToken, sfOAuthToken.getInstanceUrl()); - } catch (Exception e1) { - throw new CustomException( - new ErrorObject( - "l_s_h_sr_mr_1", - "something_went_wrong", - e.getMessage())); - } - } - throw new CustomException( - new ErrorObject( - "l_s_h_sr_mr_2", - "something_went_wrong", - e.getMessage())); - } - } -} + @Autowired + private SalesforceOAuthToken getAccessTokenService; + + @Autowired + private SalesforceOauthTokenRepository salesforceOauthTokenRepository; + + Logger logger = LoggerFactory.getLogger(SalesforceRequest.class); + + /** + * Make a request to the Salesforce API. + * @param salesforceUserId + * @param request + * @return T + */ + public T makeRequest(String salesforceUserId, SalesforceRequestInterface request) { + SalesforceOauthToken sfOAuthToken = salesforceOauthTokenRepository + .getSalesforceOauthTokenByExternalUserId(salesforceUserId); + + String decryptedAccessToken = getAccessTokenService.fetchAccessToken(sfOAuthToken); + + try { + return request.execute(decryptedAccessToken, sfOAuthToken.getInstanceUrl()); + } + catch (WebClientResponseException e) { + if (e.getStatusCode().value() == 401) { + try { + decryptedAccessToken = getAccessTokenService.updateAndGetRefreshedAccessToken(sfOAuthToken); + return request.execute(decryptedAccessToken, sfOAuthToken.getInstanceUrl()); + } + catch (Exception e1) { + logger.error("Error while refreshing access token {}", e1); + throw new CustomException(new ErrorObject("l_s_h_sr_mr_1", "something_went_wrong", e.getMessage())); + } + } + logger.error("Error while making request to salesforce {}", e); + throw new CustomException(new ErrorObject("l_s_h_sr_mr_2", "something_went_wrong", e.getMessage())); + } + } + +} \ No newline at end of file diff --git a/src/main/java/com/salessparrow/api/lib/salesforce/helper/SalesforceRequestInterface.java b/src/main/java/com/salessparrow/api/lib/salesforce/helper/SalesforceRequestInterface.java index ab9352be..bca6d324 100644 --- a/src/main/java/com/salessparrow/api/lib/salesforce/helper/SalesforceRequestInterface.java +++ b/src/main/java/com/salessparrow/api/lib/salesforce/helper/SalesforceRequestInterface.java @@ -5,5 +5,7 @@ */ @FunctionalInterface public interface SalesforceRequestInterface { - T execute(String token, String instanceUrl); + + T execute(String token, String instanceUrl); + } diff --git a/src/main/java/com/salessparrow/api/lib/salesforce/wrappers/SalesforceGetIdentity.java b/src/main/java/com/salessparrow/api/lib/salesforce/wrappers/SalesforceGetIdentity.java index d7f7d088..d05b06d7 100644 --- a/src/main/java/com/salessparrow/api/lib/salesforce/wrappers/SalesforceGetIdentity.java +++ b/src/main/java/com/salessparrow/api/lib/salesforce/wrappers/SalesforceGetIdentity.java @@ -16,29 +16,24 @@ public class SalesforceGetIdentity { - @Autowired - private SalesforceConstants salesforceConstants; - - public HttpResponse getUserIdentity(String instanceUrl, String accessToken) { - String salesforceIdentityEndpoint = instanceUrl + salesforceConstants.identityUrl(); - - Map headers = new HashMap<>(); - headers.put("Authorization", "Bearer " + accessToken); - - HttpResponse response = null; - try { - response = HttpClient.makeGetRequest( - salesforceIdentityEndpoint, - headers, - 10000); - } catch (Exception e) { - throw new CustomException( - new ErrorObject( - "l_s_w_sgi_gui_1", - "bad_request", - e.getMessage())); - } - - return response; - } + @Autowired + private SalesforceConstants salesforceConstants; + + public HttpResponse getUserIdentity(String instanceUrl, String accessToken) { + String salesforceIdentityEndpoint = instanceUrl + salesforceConstants.identityUrl(); + + Map headers = new HashMap<>(); + headers.put("Authorization", "Bearer " + accessToken); + + HttpResponse response = null; + try { + response = HttpClient.makeGetRequest(salesforceIdentityEndpoint, headers, 10000); + } + catch (Exception e) { + throw new CustomException(new ErrorObject("l_s_w_sgi_gui_1", "bad_request", e.getMessage())); + } + + return response; + } + } diff --git a/src/main/java/com/salessparrow/api/lib/salesforce/wrappers/SalesforceGetNoteContent.java b/src/main/java/com/salessparrow/api/lib/salesforce/wrappers/SalesforceGetNoteContent.java index 79d29ec5..d1a89884 100644 --- a/src/main/java/com/salessparrow/api/lib/salesforce/wrappers/SalesforceGetNoteContent.java +++ b/src/main/java/com/salessparrow/api/lib/salesforce/wrappers/SalesforceGetNoteContent.java @@ -16,38 +16,35 @@ */ @Component public class SalesforceGetNoteContent { - @Autowired - private SalesforceConstants salesforceConstants; - - @Autowired - private SalesforceRequest salesforceOauthRequest; - - /** - * Get the content of a note - * - * @param noteId - * @param salesforceUserId - * - * @return HttpResponse - */ - public HttpClient.HttpResponse getNoteContent(String noteId, String salesforceUserId){ - Integer timeoutMillis = salesforceConstants.timeoutMillis(); - - SalesforceRequestInterface request = (token, instanceUrl) -> { - String noteContentQuery = salesforceConstants.salesforceNotesContentUrl(instanceUrl, noteId); - - Map headers = new HashMap<>(); - headers.put("Authorization", "Bearer " + token); - - HttpClient.HttpResponse response = HttpClient.makeGetRequest( - noteContentQuery, - headers, - timeoutMillis); - - return response; - }; - - HttpClient.HttpResponse response = salesforceOauthRequest.makeRequest(salesforceUserId, request); - return response; - } + + @Autowired + private SalesforceConstants salesforceConstants; + + @Autowired + private SalesforceRequest salesforceOauthRequest; + + /** + * Get the content of a note + * @param noteId + * @param salesforceUserId + * @return HttpResponse + */ + public HttpClient.HttpResponse getNoteContent(String noteId, String salesforceUserId) { + Integer timeoutMillis = salesforceConstants.timeoutMillis(); + + SalesforceRequestInterface request = (token, instanceUrl) -> { + String noteContentQuery = salesforceConstants.salesforceNotesContentUrl(instanceUrl, noteId); + + Map headers = new HashMap<>(); + headers.put("Authorization", "Bearer " + token); + + HttpClient.HttpResponse response = HttpClient.makeGetRequest(noteContentQuery, headers, timeoutMillis); + + return response; + }; + + HttpClient.HttpResponse response = salesforceOauthRequest.makeRequest(salesforceUserId, request); + return response; + } + } diff --git a/src/main/java/com/salessparrow/api/lib/salesforce/wrappers/SalesforceGetRefreshedAccessToken.java b/src/main/java/com/salessparrow/api/lib/salesforce/wrappers/SalesforceGetRefreshedAccessToken.java index 62caf9f6..fd3f94bc 100644 --- a/src/main/java/com/salessparrow/api/lib/salesforce/wrappers/SalesforceGetRefreshedAccessToken.java +++ b/src/main/java/com/salessparrow/api/lib/salesforce/wrappers/SalesforceGetRefreshedAccessToken.java @@ -16,30 +16,27 @@ @Component public class SalesforceGetRefreshedAccessToken { - @Autowired - SalesforceConstants salesforceConstants; - - /** - * Get tokens from Salesforce - * - * @param decryptedRefreshToken - * - * @return HttpResponse - */ - public HttpClient.HttpResponse getRefreshedAccessToken(String decryptedRefreshToken) { - String url = salesforceConstants.oauth2Url(); - - String requestBody = "grant_type=" + salesforceConstants.refreshTokenGrantType() + "&client_id=" - + CoreConstants.salesforceClientId() - + "&client_secret=" - + CoreConstants.salesforceClientSecret() + - "&refresh_token=" + decryptedRefreshToken; - - Map headers = new HashMap<>(); - headers.put("content-type", "application/x-www-form-urlencoded"); - - HttpClient.HttpResponse response = HttpClient.makePostRequest(url, headers, requestBody, 5000); - - return response; - } + @Autowired + SalesforceConstants salesforceConstants; + + /** + * Get tokens from Salesforce + * @param decryptedRefreshToken + * @return HttpResponse + */ + public HttpClient.HttpResponse getRefreshedAccessToken(String decryptedRefreshToken) { + String url = salesforceConstants.oauth2Url(); + + String requestBody = "grant_type=" + salesforceConstants.refreshTokenGrantType() + "&client_id=" + + CoreConstants.salesforceClientId() + "&client_secret=" + CoreConstants.salesforceClientSecret() + + "&refresh_token=" + decryptedRefreshToken; + + Map headers = new HashMap<>(); + headers.put("content-type", "application/x-www-form-urlencoded"); + + HttpClient.HttpResponse response = HttpClient.makePostRequest(url, headers, requestBody, 5000); + + return response; + } + } diff --git a/src/main/java/com/salessparrow/api/lib/salesforce/wrappers/SalesforceGetTokens.java b/src/main/java/com/salessparrow/api/lib/salesforce/wrappers/SalesforceGetTokens.java deleted file mode 100644 index db73e158..00000000 --- a/src/main/java/com/salessparrow/api/lib/salesforce/wrappers/SalesforceGetTokens.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.salessparrow.api.lib.salesforce.wrappers; - -import java.util.HashMap; -import java.util.Map; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -import com.salessparrow.api.config.CoreConstants; -import com.salessparrow.api.exception.CustomException; -import com.salessparrow.api.lib.errorLib.ErrorObject; -import com.salessparrow.api.lib.globalConstants.SalesforceConstants; -import com.salessparrow.api.lib.httpLib.HttpClient; -import com.salessparrow.api.lib.httpLib.HttpClient.HttpResponse; - -/** - * SalesforceGetTokens class to get tokens from Salesforce - */ -@Component -public class SalesforceGetTokens { - - @Autowired - private SalesforceConstants salesforceConstants; - - /** - * Get tokens from Salesforce - * - * @param code - * @param redirectUri - * - * @return HttpResponse - */ - public HttpResponse getTokens(String code, String redirectUri) { - - String salesforceOAuthEndpoint = salesforceConstants.oauth2Url(); - - String requestBody = "grant_type=" + salesforceConstants.authorizationCodeGrantType() + "&client_id=" - + CoreConstants.salesforceClientId() - + "&client_secret=" - + CoreConstants.salesforceClientSecret() + - "&code=" + code + "&redirect_uri=" + redirectUri; - - Map headers = new HashMap<>(); - headers.put("content-type", "application/x-www-form-urlencoded"); - - HttpResponse response = null; - try { - response = HttpClient.makePostRequest( - salesforceOAuthEndpoint, - headers, - requestBody, - 10000); - } catch (Exception e) { - throw new CustomException( - new ErrorObject( - "l_s_w_sgt_gt_1", - "bad_request", - e.getMessage())); - } - return response; - } -} diff --git a/src/main/java/com/salessparrow/api/lib/salesforce/wrappers/SalesforceTokens.java b/src/main/java/com/salessparrow/api/lib/salesforce/wrappers/SalesforceTokens.java new file mode 100644 index 00000000..1d7c0764 --- /dev/null +++ b/src/main/java/com/salessparrow/api/lib/salesforce/wrappers/SalesforceTokens.java @@ -0,0 +1,82 @@ +package com.salessparrow.api.lib.salesforce.wrappers; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.salessparrow.api.config.CoreConstants; +import com.salessparrow.api.exception.CustomException; +import com.salessparrow.api.lib.errorLib.ErrorObject; +import com.salessparrow.api.lib.errorLib.ParamErrorObject; +import com.salessparrow.api.lib.globalConstants.SalesforceConstants; +import com.salessparrow.api.lib.httpLib.HttpClient; +import com.salessparrow.api.lib.httpLib.HttpClient.HttpResponse; +import java.util.List; + +/** + * SalesforceTokens class to handle token operations with Salesforce + */ +@Component +public class SalesforceTokens { + + @Autowired + private SalesforceConstants salesforceConstants; + + /** + * Get tokens from Salesforce using the authorization code. + * @param code + * @param redirectUri + * @return HttpResponse + */ + public HttpResponse getTokens(String code, String redirectUri) { + + String salesforceOAuthEndpoint = salesforceConstants.oauth2Url(); + + String requestBody = "grant_type=" + salesforceConstants.authorizationCodeGrantType() + "&client_id=" + + CoreConstants.salesforceClientId() + "&client_secret=" + CoreConstants.salesforceClientSecret() + + "&code=" + code + "&redirect_uri=" + redirectUri; + + Map headers = new HashMap<>(); + headers.put("content-type", "application/x-www-form-urlencoded"); + + HttpResponse response = null; + try { + response = HttpClient.makePostRequest(salesforceOAuthEndpoint, headers, requestBody, 10000); + } + catch (Exception e) { + List paramErrorIdentifiers = new ArrayList<>(); + paramErrorIdentifiers.add("invalid_code"); + + throw new CustomException(new ParamErrorObject("l_s_w_sgt_gt_1", e.getMessage(), paramErrorIdentifiers)); + } + return response; + } + + /** + * Revokes tokens from Salesforce using the access/refresh token. + * @param instanceUrl Instance URL + * @param token Refresh token + * @return HttpResponse + */ + public HttpResponse revokeTokens(String instanceUrl, String token) { + String salesforceRevokeTokensEndpoint = instanceUrl + salesforceConstants.revokeTokensUrl(); + + String requestBody = "token=" + token; + + Map headers = new HashMap<>(); + headers.put("content-type", "application/x-www-form-urlencoded"); + + HttpResponse response = null; + try { + response = HttpClient.makePostRequest(salesforceRevokeTokensEndpoint, headers, requestBody, 10000); + } + catch (Exception e) { + throw new CustomException(new ErrorObject("l_s_w_srt_rt_1", "something_went_wrong", e.getMessage())); + } + return response; + } + +} \ No newline at end of file diff --git a/src/main/java/com/salessparrow/api/lib/validators/DateFormatValidator.java b/src/main/java/com/salessparrow/api/lib/validators/DateFormatValidator.java new file mode 100644 index 00000000..0e42b15d --- /dev/null +++ b/src/main/java/com/salessparrow/api/lib/validators/DateFormatValidator.java @@ -0,0 +1,43 @@ +package com.salessparrow.api.lib.validators; + +import java.text.SimpleDateFormat; +import java.util.Date; + +import com.salessparrow.api.lib.customAnnotations.ValidDateFormat; + +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; + +/** + * Validate date format with regular expression + * + */ +public class DateFormatValidator implements ConstraintValidator { + + private static final String DATE_FORMAT = "yyyy-MM-dd"; + + /** + * Validate date format with regular expression + * @param date date address for validation + * @return true valid date format, false invalid date format + */ + @Override + public boolean isValid(String value, ConstraintValidatorContext context) { + if (value == null) { + return false; + } + + SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT); + sdf.setLenient(false); + + try { + Date parsedDate = sdf.parse(value); + String dateString = sdf.format(parsedDate); + return dateString.equals(value.toString()); + } + catch (Exception ex) { + return false; + } + } + +} \ No newline at end of file diff --git a/src/main/java/com/salessparrow/api/lib/validators/RedirectUriValidator.java b/src/main/java/com/salessparrow/api/lib/validators/RedirectUriValidator.java index 7e8fd95e..cbd2dc2e 100644 --- a/src/main/java/com/salessparrow/api/lib/validators/RedirectUriValidator.java +++ b/src/main/java/com/salessparrow/api/lib/validators/RedirectUriValidator.java @@ -13,11 +13,11 @@ public class RedirectUriValidator implements ConstraintValidator { - private List ALLOWED_URIS = new ArrayList<>( - Arrays.asList(CoreConstants.getWhitelistedRedirectUris())); + private List ALLOWED_URIS = new ArrayList<>(Arrays.asList(CoreConstants.getWhitelistedRedirectUris())); + + @Override + public boolean isValid(String value, ConstraintValidatorContext context) { + return value != null && ALLOWED_URIS.contains(value); + } - @Override - public boolean isValid(String value, ConstraintValidatorContext context) { - return value != null && ALLOWED_URIS.contains(value); - } } \ No newline at end of file diff --git a/src/main/java/com/salessparrow/api/lib/wrappers/SanitizedRequestWrapper.java b/src/main/java/com/salessparrow/api/lib/wrappers/SanitizedRequestWrapper.java new file mode 100644 index 00000000..e7f2a1c1 --- /dev/null +++ b/src/main/java/com/salessparrow/api/lib/wrappers/SanitizedRequestWrapper.java @@ -0,0 +1,194 @@ +package com.salessparrow.api.lib.wrappers; + +import jakarta.servlet.ReadListener; +import jakarta.servlet.ServletInputStream; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequestWrapper; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Custom request wrapper to sanitize the request body + */ +public class SanitizedRequestWrapper extends HttpServletRequestWrapper { + + Logger logger = LoggerFactory.getLogger(SanitizedRequestWrapper.class); + + private final String sanitizedBody; + + private Map> sanitizedParams; + + private Map> sanitizedHeaders; + + public SanitizedRequestWrapper(HttpServletRequest request, String sanitizedBody) { + super(request); + this.sanitizedBody = sanitizedBody; + this.sanitizedParams = new HashMap<>(); + this.sanitizedHeaders = new HashMap<>(); + } + + /** + * Method to get the request body as buffered reader + */ + @Override + public BufferedReader getReader() throws IOException { + return new BufferedReader(new InputStreamReader(new ByteArrayInputStream(sanitizedBody.getBytes()))); + } + + /** + * Method to get the request body as input stream + */ + @Override + public ServletInputStream getInputStream() throws IOException { + final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(sanitizedBody.getBytes()); + + return new ServletInputStream() { + @Override + public int read() throws IOException { + return byteArrayInputStream.read(); + } + + @Override + public boolean isFinished() { + return byteArrayInputStream.available() == 0; + } + + @Override + public boolean isReady() { + return true; + } + + @Override + public void setReadListener(ReadListener readListener) { + throw new UnsupportedOperationException("Not implemented"); + } + }; + } + + /** + * Method to set the request parameter value by key + * @param key + * @param value + * @return void + */ + public void setParameter(String key, String value) { + List values = this.sanitizedParams.getOrDefault(key, new ArrayList()); + values.add(value); + this.sanitizedParams.put(key, values); + } + + /** + * Method to get the request parameter value by key from the sanitized params map. + * @param key - parameter key + * @return String - parameter value + */ + @Override + public String getParameter(String key) { + List values = this.sanitizedParams.get(key); + if (values != null && !values.isEmpty()) { + return values.get(0); + } + + return null; + } + + /** + * Method to get the request parameter map from the sanitized params map. + * @return Map - parameter map + */ + @Override + public Map getParameterMap() { + Map sanitizedParamsMap = new HashMap<>(); + this.sanitizedParams.forEach((key, values) -> { + String[] stringValues = values.toArray(new String[0]); + sanitizedParamsMap.put(key, stringValues); + }); + + return sanitizedParamsMap; + } + + /** + * Method to get the request parameter names from the sanitized params map. + * @return Enumeration - parameter names + */ + @Override + public Enumeration getParameterNames() { + return Collections.enumeration(this.sanitizedParams.keySet()); + } + + /** + * Method to get the request parameter values by key from the sanitized params map. + * @param key - parameter key + * @return String[] - parameter values + */ + @Override + public String[] getParameterValues(String key) { + List values = this.sanitizedParams.get(key); + if (values != null && !values.isEmpty()) { + return values.toArray(new String[0]); + } + return null; + } + + /** + * Method to get the request parameter map + * @param key - header key + * @param value - header value + * @return void + */ + public void setHeader(String key, String value) { + List headerValues = this.sanitizedHeaders.getOrDefault(key, new ArrayList()); + headerValues.add(value); + this.sanitizedHeaders.put(key, headerValues); + } + + /** + * Method to get the request header value by key from the sanitized headers map. + * @param key - header key + * @return String - header value + */ + @Override + public String getHeader(String key) { + List values = this.sanitizedHeaders.get(key); + if (values != null && !values.isEmpty()) { + return values.get(0); + } + return null; + } + + /** + * Method to get the request header names from the sanitized headers map. + * @return Enumeration - header names + * @return void + */ + @Override + public Enumeration getHeaderNames() { + return Collections.enumeration(this.sanitizedHeaders.keySet()); + } + + /** + * Method to get the request header values by key from the sanitized headers map. + * @param key - header key + * @return Enumeration - header values + */ + @Override + public Enumeration getHeaders(String key) { + List values = this.sanitizedHeaders.get(key); + if (values != null && !values.isEmpty()) { + return Collections.enumeration(values); + } + return Collections.enumeration(Collections.emptyList()); + } + +} diff --git a/src/main/java/com/salessparrow/api/repositories/SalesforceOauthTokenRepository.java b/src/main/java/com/salessparrow/api/repositories/SalesforceOauthTokenRepository.java index addf09c9..e5896a6b 100644 --- a/src/main/java/com/salessparrow/api/repositories/SalesforceOauthTokenRepository.java +++ b/src/main/java/com/salessparrow/api/repositories/SalesforceOauthTokenRepository.java @@ -1,13 +1,13 @@ package com.salessparrow.api.repositories; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.cache.annotation.CachePut; +import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Repository; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper; import com.salessparrow.api.domain.SalesforceOauthToken; import com.salessparrow.api.exception.CustomException; +import com.salessparrow.api.lib.Util; import com.salessparrow.api.lib.errorLib.ErrorObject; import com.salessparrow.api.lib.globalConstants.CacheConstants; @@ -17,48 +17,78 @@ @Repository public class SalesforceOauthTokenRepository { - @Autowired - private DynamoDBMapper dynamoDBMapper; + private final DynamoDBMapper dynamoDBMapper; - /** - * Saves a SalesforceOauthToken to the salesforce_oauth_tokens table. - * - * @param salesforceOauthToken - * - * @return SalesforceOauthToken - */ - @CachePut(value = CacheConstants.SS_SALESFORCE_OAUTH_TOKEN_CACHE, key = "#salesforceOauthToken.externalUserId") - public SalesforceOauthToken saveSalesforceOauthToken(SalesforceOauthToken salesforceOauthToken) { - try { - dynamoDBMapper.save(salesforceOauthToken); - } catch (Exception e) { - throw new CustomException(new ErrorObject( - "r_sotr_ssot_1", - "something_went_wrong", - e.getMessage())); - } - return salesforceOauthToken; - } + public SalesforceOauthTokenRepository(DynamoDBMapper dynamoDBMapper) { + this.dynamoDBMapper = dynamoDBMapper; + } - /** - * Retrieves a SalesforceOauthToken from the salesforce_oauth_tokens table based - * on the - * provided externalUserId. - * - * @param externalUserId - * - * @return SalesforceOauthToken - */ - @Cacheable(value = CacheConstants.SS_SALESFORCE_OAUTH_TOKEN_CACHE, key = "#externalUserId") - public SalesforceOauthToken getSalesforceOauthTokenByExternalUserId(String externalUserId) { - try { - return dynamoDBMapper.load(SalesforceOauthToken.class, externalUserId); - } catch (Exception e) { - throw new CustomException( - new ErrorObject( - "r_sotr_gsotbsfui_1", - "something_went_wrong", - e.getMessage())); - } - } -} + /** + * Insert a SalesforceOauthToken to the salesforce_oauth_tokens table. + * @param salesforceOauthToken + * @return SalesforceOauthToken + */ + @CacheEvict(value = CacheConstants.SS_SALESFORCE_OAUTH_TOKEN_CACHE, key = "#salesforceOauthToken.externalUserId") + public SalesforceOauthToken createSalesforceOauthToken(SalesforceOauthToken salesforceOauthToken) { + // Create a row with status active and created at as current time + salesforceOauthToken.setStatus(SalesforceOauthToken.Status.ACTIVE); + salesforceOauthToken.setCreatedAt(Util.getCurrentTimeInDateFormat()); + + try { + dynamoDBMapper.save(salesforceOauthToken); + } + catch (Exception e) { + throw new CustomException(new ErrorObject("r_sotr_csot_1", "something_went_wrong", e.getMessage())); + } + return salesforceOauthToken; + } + + /** + * Saves a SalesforceOauthToken to the salesforce_oauth_tokens table. + * @param salesforceOauthToken + * @return SalesforceOauthToken + */ + @CacheEvict(value = CacheConstants.SS_SALESFORCE_OAUTH_TOKEN_CACHE, key = "#salesforceOauthToken.externalUserId") + public SalesforceOauthToken updateSalesforceOauthToken(SalesforceOauthToken salesforceOauthToken) { + try { + dynamoDBMapper.save(salesforceOauthToken); + } + catch (Exception e) { + throw new CustomException(new ErrorObject("r_sotr_ssot_1", "something_went_wrong", e.getMessage())); + } + return salesforceOauthToken; + } + + /** + * Retrieves a SalesforceOauthToken from the salesforce_oauth_tokens table based on + * the provided externalUserId. + * @param externalUserId + * @return SalesforceOauthToken + */ + @Cacheable(value = CacheConstants.SS_SALESFORCE_OAUTH_TOKEN_CACHE, key = "#externalUserId") + public SalesforceOauthToken getSalesforceOauthTokenByExternalUserId(String externalUserId) { + try { + return dynamoDBMapper.load(SalesforceOauthToken.class, externalUserId); + } + catch (Exception e) { + throw new CustomException(new ErrorObject("r_sotr_gsotbeui_1", "something_went_wrong", e.getMessage())); + } + } + + /** + * Deletes a SalesforceOauthToken from the salesforce_oauth_tokens table based on the + * provided SalesforceOauthToken. + * @param salesforceOauthToken + * @return void + */ + @CacheEvict(value = CacheConstants.SS_SALESFORCE_OAUTH_TOKEN_CACHE, key = "#salesforceOauthToken.externalUserId") + public void deleteSalesforceOauthTokenBySalesforceOauthToken(SalesforceOauthToken salesforceOauthToken) { + try { + dynamoDBMapper.delete(salesforceOauthToken); + } + catch (Exception e) { + throw new CustomException(new ErrorObject("r_sotr_dsotbeui_1", "something_went_wrong", e.getMessage())); + } + } + +} \ No newline at end of file diff --git a/src/main/java/com/salessparrow/api/repositories/SalesforceOrganizationRepository.java b/src/main/java/com/salessparrow/api/repositories/SalesforceOrganizationRepository.java index 8e66e669..25b874c0 100644 --- a/src/main/java/com/salessparrow/api/repositories/SalesforceOrganizationRepository.java +++ b/src/main/java/com/salessparrow/api/repositories/SalesforceOrganizationRepository.java @@ -3,9 +3,8 @@ import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper; import com.salessparrow.api.domain.SalesforceOrganization; import com.salessparrow.api.exception.CustomException; +import com.salessparrow.api.lib.Util; import com.salessparrow.api.lib.errorLib.ErrorObject; - -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; /** @@ -14,45 +13,59 @@ @Repository public class SalesforceOrganizationRepository { - @Autowired - private DynamoDBMapper dynamoDBMapper; - - /** - * Saves a SalesforceOrganization to the salesforce_organizations table. - * - * @param sfo - * - * @return SalesforceOrganization - */ - public SalesforceOrganization saveSalesforceOrganization(SalesforceOrganization sfo) { - try { - dynamoDBMapper.save(sfo); - } catch (Exception e) { - throw new CustomException(new ErrorObject( - "r_sor_sso_1", - "something_went_wrong", - e.getMessage())); - } - return sfo; - } - - /** - * Gets a SalesforceOrganization from the salesforce_organizations table by - * externalOrganizationId. - * - * @param externalOrganizationId - * - * @return SalesforceOrganization - */ - public SalesforceOrganization getSalesforceOrganizationByExternalOrganizationId(String externalOrganizationId) { - try { - return dynamoDBMapper.load(SalesforceOrganization.class, externalOrganizationId); - } catch (Exception e) { - throw new CustomException( - new ErrorObject( - "r_sor_gsobeoi_1", - "something_went_wrong", - e.getMessage())); - } - } + private final DynamoDBMapper dynamoDBMapper; + + public SalesforceOrganizationRepository(DynamoDBMapper dynamoDBMapper) { + this.dynamoDBMapper = dynamoDBMapper; + } + + /** + * Insert a SalesforceOrganization to the salesforce_organizations table. + * @param sfo + * @return SalesforceOrganization + */ + public SalesforceOrganization createSalesforceOrganization(SalesforceOrganization sfo) { + // Create a row with status active and created at as current time + sfo.setStatus(SalesforceOrganization.Status.ACTIVE); + sfo.setCreatedAt(Util.getCurrentTimeInDateFormat()); + + try { + dynamoDBMapper.save(sfo); + } + catch (Exception e) { + throw new CustomException(new ErrorObject("r_sor_cso_1", "something_went_wrong", e.getMessage())); + } + return sfo; + } + + /** + * Update a SalesforceOrganization to the salesforce_organizations table. + * @param sfo + * @return SalesforceOrganization + */ + public SalesforceOrganization updateSalesforceOrganization(SalesforceOrganization sfo) { + try { + dynamoDBMapper.save(sfo); + } + catch (Exception e) { + throw new CustomException(new ErrorObject("r_sor_sso_1", "something_went_wrong", e.getMessage())); + } + return sfo; + } + + /** + * Gets a SalesforceOrganization from the salesforce_organizations table by + * externalOrganizationId. + * @param externalOrganizationId + * @return SalesforceOrganization + */ + public SalesforceOrganization getSalesforceOrganizationByExternalOrganizationId(String externalOrganizationId) { + try { + return dynamoDBMapper.load(SalesforceOrganization.class, externalOrganizationId); + } + catch (Exception e) { + throw new CustomException(new ErrorObject("r_sor_gsobeoi_1", "something_went_wrong", e.getMessage())); + } + } + } diff --git a/src/main/java/com/salessparrow/api/repositories/SalesforceUserRepository.java b/src/main/java/com/salessparrow/api/repositories/SalesforceUserRepository.java index 35c583e9..97043c58 100644 --- a/src/main/java/com/salessparrow/api/repositories/SalesforceUserRepository.java +++ b/src/main/java/com/salessparrow/api/repositories/SalesforceUserRepository.java @@ -1,13 +1,14 @@ package com.salessparrow.api.repositories; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.cache.annotation.CachePut; +import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Repository; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper; +import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapperConfig; import com.salessparrow.api.domain.SalesforceUser; import com.salessparrow.api.exception.CustomException; +import com.salessparrow.api.lib.Util; import com.salessparrow.api.lib.errorLib.ErrorObject; import com.salessparrow.api.lib.globalConstants.CacheConstants; @@ -17,48 +18,84 @@ @Repository public class SalesforceUserRepository { - @Autowired - private DynamoDBMapper dynamoDBMapper; + private final DynamoDBMapper dynamoDBMapper; - /** - * Saves a SalesforceUser to the salesforce_users table. - * - * @param salesforceUser - * - * @return SalesforceUser - */ - @CachePut(value=CacheConstants.SS_SALESFORCE_USER_CACHE, key="#salesforceUser.externalUserId") - public SalesforceUser saveSalesforceUser(SalesforceUser salesforceUser) { - try { - dynamoDBMapper.save(salesforceUser); - } catch (Exception e) { - throw new CustomException( - new ErrorObject( - "r_sur_ssu_1", - "something_went_wrong", - e.getMessage())); - } - return salesforceUser; - } + public SalesforceUserRepository(DynamoDBMapper dynamoDBMapper) { + this.dynamoDBMapper = dynamoDBMapper; + } - /** - * Retrieves a SalesforceUser from the salesforce_users table based on the - * provided id. - * - * @param id - * - * @return SalesforceUser - */ - @Cacheable(value=CacheConstants.SS_SALESFORCE_USER_CACHE, key="#externalUserId") - public SalesforceUser getSalesforceUserByExternalUserId(String externalUserId) { - try { - return dynamoDBMapper.load(SalesforceUser.class, externalUserId); - } catch (Exception e) { - throw new CustomException( - new ErrorObject( - "r_sur_gsubi_1", - "something_went_wrong", - e.getMessage())); - } - } -} + /** + * Insert a SalesforceUser to the salesforce_users table. + * @param salesforceUser + * @return SalesforceUser + */ + @CacheEvict(value = CacheConstants.SS_SALESFORCE_USER_CACHE, key = "#salesforceUser.externalUserId") + public SalesforceUser createSalesforceUser(SalesforceUser salesforceUser) { + // Create a row with status active and created at as current time + salesforceUser.setStatus(SalesforceUser.Status.ACTIVE); + salesforceUser.setCreatedAt(Util.getCurrentTimeInDateFormat()); + + try { + dynamoDBMapper.save(salesforceUser); + } + catch (Exception e) { + throw new CustomException(new ErrorObject("r_sur_csu_1", "something_went_wrong", e.getMessage())); + } + return salesforceUser; + } + + /** + * Updates a SalesforceUser to the salesforce_users table. + * @param salesforceUser + * @return SalesforceUser + */ + @CacheEvict(value = CacheConstants.SS_SALESFORCE_USER_CACHE, key = "#salesforceUser.externalUserId") + public SalesforceUser updateSalesforceUser(SalesforceUser salesforceUser) { + try { + dynamoDBMapper.save(salesforceUser); + } + catch (Exception e) { + throw new CustomException(new ErrorObject("r_sur_usu_1", "something_went_wrong", e.getMessage())); + } + return salesforceUser; + } + + /** + * Retrieves a SalesforceUser from the salesforce_users table based on the provided + * id. + * @param id + * @return SalesforceUser + */ + @Cacheable(value = CacheConstants.SS_SALESFORCE_USER_CACHE, key = "#externalUserId") + public SalesforceUser getSalesforceUserByExternalUserId(String externalUserId) { + try { + return dynamoDBMapper.load(SalesforceUser.class, externalUserId); + } + catch (Exception e) { + throw new CustomException(new ErrorObject("r_sur_gsubi_1", "something_went_wrong", e.getMessage())); + } + } + + @CacheEvict(value = CacheConstants.SS_SALESFORCE_USER_CACHE, key = "#externalUserId") + public void removeSalesforceUserData(String externalUserId) { + SalesforceUser salesforceUser = getSalesforceUserByExternalUserId(externalUserId); + salesforceUser.setIdentityUrl(null); + salesforceUser.setExternalOrganizationId(null); + salesforceUser.setName(null); + salesforceUser.setEmail(null); + salesforceUser.setUserKind(null); + salesforceUser.setCookieToken(null); + salesforceUser.setEncryptionSalt(null); + salesforceUser.setStatus(SalesforceUser.Status.DELETED); + + try { + dynamoDBMapper.save(salesforceUser, + new DynamoDBMapperConfig.Builder().withSaveBehavior(DynamoDBMapperConfig.SaveBehavior.UPDATE) + .build()); + } + catch (Exception e) { + throw new CustomException(new ErrorObject("r_sur_r_1", "something_went_wrong", e.getMessage())); + } + } + +} \ No newline at end of file diff --git a/src/main/java/com/salessparrow/api/services/accountNotes/CreateAccountNoteService.java b/src/main/java/com/salessparrow/api/services/accountNotes/CreateAccountNoteService.java new file mode 100644 index 00000000..c7ab77aa --- /dev/null +++ b/src/main/java/com/salessparrow/api/services/accountNotes/CreateAccountNoteService.java @@ -0,0 +1,35 @@ +package com.salessparrow.api.services.accountNotes; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import com.salessparrow.api.domain.SalesforceUser; +import com.salessparrow.api.dto.formatter.CreateNoteFormatterDto; +import com.salessparrow.api.dto.requestMapper.NoteDto; +import com.salessparrow.api.lib.crmActions.createAccountNote.CreateNoteFactory; + +import jakarta.servlet.http.HttpServletRequest; + +/** + * CreateNoteService is a service class for the create note action for the CRM. + */ +@Service +public class CreateAccountNoteService { + + @Autowired + private CreateNoteFactory createNoteFactory; + + /** + * Create a note for a specific account. + * @param request + * @param accountId + * @return CreateNoteFormatterDto + */ + public CreateNoteFormatterDto createNote(HttpServletRequest request, String accountId, NoteDto note) { + + SalesforceUser currentUser = (SalesforceUser) request.getAttribute("current_user"); + + return createNoteFactory.createNote(currentUser, accountId, note); + } + +} diff --git a/src/main/java/com/salessparrow/api/services/accountNotes/DeleteAccountNoteService.java b/src/main/java/com/salessparrow/api/services/accountNotes/DeleteAccountNoteService.java new file mode 100644 index 00000000..2c5843b9 --- /dev/null +++ b/src/main/java/com/salessparrow/api/services/accountNotes/DeleteAccountNoteService.java @@ -0,0 +1,38 @@ +package com.salessparrow.api.services.accountNotes; + +import org.slf4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import com.salessparrow.api.domain.User; +import com.salessparrow.api.lib.crmActions.deleteAccountNote.DeleteAccountNoteFactory; + +import jakarta.servlet.http.HttpServletRequest; + +/** + * DeleteAccountNoteService is a service class for the DeleteAccountNote action for the + * CRM. + */ +@Service +public class DeleteAccountNoteService { + + private Logger logger = org.slf4j.LoggerFactory.getLogger(DeleteAccountNoteService.class); + + @Autowired + private DeleteAccountNoteFactory deleteAccountNoteFactory; + + /** + * Delete note for the given note id + * @param accountId + * @param noteId + * @return void + */ + public void deleteAccountNote(HttpServletRequest request, String accountId, String noteId) { + logger.info("Delete Account Note Service called"); + + User currentUser = (User) request.getAttribute("current_user"); + + deleteAccountNoteFactory.deleteAccountNote(currentUser, noteId); + } + +} diff --git a/src/main/java/com/salessparrow/api/services/accountNotes/GetAccountNoteDetailsService.java b/src/main/java/com/salessparrow/api/services/accountNotes/GetAccountNoteDetailsService.java new file mode 100644 index 00000000..90c2ddb6 --- /dev/null +++ b/src/main/java/com/salessparrow/api/services/accountNotes/GetAccountNoteDetailsService.java @@ -0,0 +1,33 @@ +package com.salessparrow.api.services.accountNotes; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import com.salessparrow.api.domain.User; +import com.salessparrow.api.dto.formatter.GetNoteDetailsFormatterDto; +import com.salessparrow.api.lib.crmActions.getAccountNoteDetails.GetAccountNoteDetailsFactory; + +import jakarta.servlet.http.HttpServletRequest; + +/** + * GetNoteDetailsService is a service class for the GetNoteDetails action for the CRM. + */ +@Service +public class GetAccountNoteDetailsService { + + @Autowired + private GetAccountNoteDetailsFactory getNoteDetailsFactory; + + /** + * Get the details of a note + * @param accountId + * @param noteId + * @return GetNoteDetailsFormatterDto + */ + public GetNoteDetailsFormatterDto getNoteDetails(HttpServletRequest request, String noteId) { + User currentUser = (User) request.getAttribute("current_user"); + + return getNoteDetailsFactory.getNoteDetails(currentUser, noteId); + } + +} diff --git a/src/main/java/com/salessparrow/api/services/accountNotes/GetAccountNotesListService.java b/src/main/java/com/salessparrow/api/services/accountNotes/GetAccountNotesListService.java new file mode 100644 index 00000000..3b6458ae --- /dev/null +++ b/src/main/java/com/salessparrow/api/services/accountNotes/GetAccountNotesListService.java @@ -0,0 +1,34 @@ +package com.salessparrow.api.services.accountNotes; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import jakarta.servlet.http.HttpServletRequest; + +import com.salessparrow.api.domain.User; +import com.salessparrow.api.dto.formatter.GetNotesListFormatterDto; +import com.salessparrow.api.lib.crmActions.getAccountNotesList.GetAccountNoteListFactory; + +/** + * GetNotesListService is a service class for the GetNotesList action for the CRM. + */ +@Service +public class GetAccountNotesListService { + + @Autowired + private GetAccountNoteListFactory getNotesListFactory; + + /** + * Get the list of notes for a given account + * @param accountId + * @param request + * @return GetNotesListFormatterDto + **/ + public GetNotesListFormatterDto getNotesList(HttpServletRequest request, String accountId) { + + User currentUser = (User) request.getAttribute("current_user"); + + return getNotesListFactory.getNotesList(currentUser, accountId); + } + +} diff --git a/src/main/java/com/salessparrow/api/services/accountTask/CreateTaskService.java b/src/main/java/com/salessparrow/api/services/accountTask/CreateTaskService.java new file mode 100644 index 00000000..d30cb0ab --- /dev/null +++ b/src/main/java/com/salessparrow/api/services/accountTask/CreateTaskService.java @@ -0,0 +1,40 @@ +package com.salessparrow.api.services.accountTask; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import com.salessparrow.api.domain.User; +import com.salessparrow.api.dto.formatter.CreateTaskFormatterDto; +import com.salessparrow.api.dto.requestMapper.CreateAccountTaskDto; +import com.salessparrow.api.lib.crmActions.createAccountTask.CreateAccountTaskFactory; + +import jakarta.servlet.http.HttpServletRequest; + +/** + * CreateTaskService class is responsible for creating a task in CRM + */ +@Service +public class CreateTaskService { + + Logger logger = LoggerFactory.getLogger(CreateTaskService.class); + + @Autowired + private CreateAccountTaskFactory createAccountTaskFactory; + + /** + * Create a task in CRM + * @param request HttpServletRequest object + * @param accountId CRM account id + * @param task CreateTaskDto object + * @return CreateTaskFormatterDto object + */ + public CreateTaskFormatterDto createAccountTask(HttpServletRequest request, String accountId, + CreateAccountTaskDto task) { + logger.info("inside createTask Service"); + User currentUser = (User) request.getAttribute("current_user"); + return createAccountTaskFactory.createAccountTask(currentUser, accountId, task); + } + +} diff --git a/src/main/java/com/salessparrow/api/services/accountTask/DeleteTaskService.java b/src/main/java/com/salessparrow/api/services/accountTask/DeleteTaskService.java new file mode 100644 index 00000000..4cd7c5f7 --- /dev/null +++ b/src/main/java/com/salessparrow/api/services/accountTask/DeleteTaskService.java @@ -0,0 +1,39 @@ +package com.salessparrow.api.services.accountTask; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import com.salessparrow.api.domain.User; +import com.salessparrow.api.lib.crmActions.deleteAccountTask.DeleteAccountTaskFactory; + +import jakarta.servlet.http.HttpServletRequest; + +/** + * DeleteTaskService is a service class that handles the deleting a task in an account. + */ +@Service +public class DeleteTaskService { + + Logger logger = LoggerFactory.getLogger(DeleteTaskService.class); + + @Autowired + private DeleteAccountTaskFactory deleteAccountTaskFactory; + + /** + * Deletes a task in an account. + * @param request + * @param accountId + * @param taskId + * @return void + */ + public void deleteAccountTask(HttpServletRequest request, String accountId, String taskId) { + logger.info("Delete task in account service called"); + + User currentUser = (User) request.getAttribute("current_user"); + + deleteAccountTaskFactory.deleteAccountTask(currentUser, accountId, taskId); + } + +} diff --git a/src/main/java/com/salessparrow/api/services/accountTask/GetAccountTasksListService.java b/src/main/java/com/salessparrow/api/services/accountTask/GetAccountTasksListService.java new file mode 100644 index 00000000..162f5496 --- /dev/null +++ b/src/main/java/com/salessparrow/api/services/accountTask/GetAccountTasksListService.java @@ -0,0 +1,39 @@ +package com.salessparrow.api.services.accountTask; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import com.salessparrow.api.domain.User; +import com.salessparrow.api.dto.formatter.GetTasksListFormatterDto; +import com.salessparrow.api.lib.crmActions.getAccountTasksList.GetAccountTasksListFactory; + +import jakarta.servlet.http.HttpServletRequest; + +/** + * GetAccountTasksListService class is responsible for getting list of account tasks in + * CRM + */ +@Service +public class GetAccountTasksListService { + + Logger logger = LoggerFactory.getLogger(GetAccountTasksListService.class); + + @Autowired + private GetAccountTasksListFactory getAccountTasksListFactory; + + /** + * Get list of account tasks from CRM + * @param request HttpServletRequest object + * @param accountId CRM account id + * @return GetTasksListFormatterDto object + */ + public GetTasksListFormatterDto getAccountTasksList(HttpServletRequest request, String accountId) { + logger.info("getAccountTasksList Service called"); + + User currentUser = (User) request.getAttribute("current_user"); + return getAccountTasksListFactory.getAccountTasksList(currentUser, accountId); + } + +} diff --git a/src/main/java/com/salessparrow/api/services/accounts/CreateNoteService.java b/src/main/java/com/salessparrow/api/services/accounts/CreateNoteService.java deleted file mode 100644 index 5793ebcf..00000000 --- a/src/main/java/com/salessparrow/api/services/accounts/CreateNoteService.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.salessparrow.api.services.accounts; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -import com.salessparrow.api.domain.SalesforceUser; -import com.salessparrow.api.dto.formatter.CreateNoteFormatterDto; -import com.salessparrow.api.dto.requestMapper.NoteDto; -import com.salessparrow.api.lib.crmActions.createNote.CreateNoteFactory; - -import jakarta.servlet.http.HttpServletRequest; - -/** - * CreateNoteService is a service class for the create note action for the CRM. - */ -@Service -public class CreateNoteService { - - @Autowired - private CreateNoteFactory createNoteFactory; - - /** - * Create a note for a specific account. - * - * @param request - * @param accountId - * @return CreateNoteFormatterDto - */ - public CreateNoteFormatterDto createNote(HttpServletRequest request, String accountId, NoteDto note) { - - SalesforceUser currentUser = (SalesforceUser) request.getAttribute("current_user"); - - return createNoteFactory.createNote(currentUser, accountId, note); - } -} diff --git a/src/main/java/com/salessparrow/api/services/accounts/GetAccountListService.java b/src/main/java/com/salessparrow/api/services/accounts/GetAccountListService.java index 02744131..1b4b9546 100644 --- a/src/main/java/com/salessparrow/api/services/accounts/GetAccountListService.java +++ b/src/main/java/com/salessparrow/api/services/accounts/GetAccountListService.java @@ -1,15 +1,14 @@ package com.salessparrow.api.services.accounts; -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.salessparrow.api.domain.User; import com.salessparrow.api.dto.formatter.GetAccountsFormatterDto; import com.salessparrow.api.dto.requestMapper.GetAccountsDto; +import com.salessparrow.api.dto.responseMapper.GetAccountListResponseDto; import com.salessparrow.api.lib.crmActions.getAccounts.GetAccountsFactory; +import com.salessparrow.api.lib.globalConstants.AccountConstants; import jakarta.servlet.http.HttpServletRequest; @@ -18,29 +17,39 @@ */ @Service public class GetAccountListService { - @Autowired - private GetAccountsFactory getAccountsFactory; - - /** - * Get the list of accounts for a given search term - * @param request - * @param getAccountsDto - * - * @return GetAccountsFormatterDto - **/ - public GetAccountsFormatterDto getAccounts(HttpServletRequest request, GetAccountsDto getAccountsDto) { - User currentUser = (User) request.getAttribute("current_user"); - - String formattedSearchString = formatSearchString(getAccountsDto.getQ()); - return getAccountsFactory.getAccounts(currentUser, formattedSearchString); - } - - private String formatSearchString(String q) { - q = q.trim(); - try { - return URLEncoder.encode(q, "UTF-8"); - } catch (UnsupportedEncodingException e) { - return q; - } - } -} + + @Autowired + private GetAccountsFactory getAccountsFactory; + + /** + * Get the list of accounts for a given search term + * @param request + * @param getAccountsDto + * @return GetAccountsFormatterDto + **/ + public GetAccountListResponseDto getAccounts(HttpServletRequest request, GetAccountsDto getAccountsDto) { + User currentUser = (User) request.getAttribute("current_user"); + + String formattedSearchString = ""; + if (getAccountsDto.getQ() != null) { + formattedSearchString = formatSearchString(getAccountsDto.getQ()); + } + GetAccountsFormatterDto getAccountsFormatterDto = getAccountsFactory.getAccounts(currentUser, + formattedSearchString, AccountConstants.BASIC_VIEW_KIND, 0); + + GetAccountListResponseDto getAccountListResponseDto = new GetAccountListResponseDto(); + if (getAccountsFormatterDto.getAccountIds() != null) { + getAccountListResponseDto.setAccountIds(getAccountsFormatterDto.getAccountIds()); + } + if (getAccountsFormatterDto.getAccountMapById() != null) { + getAccountListResponseDto.setAccountMapById(getAccountsFormatterDto.getAccountMapById()); + } + return getAccountListResponseDto; + } + + private String formatSearchString(String q) { + q = q.trim(); + return q; + } + +} \ No newline at end of file diff --git a/src/main/java/com/salessparrow/api/services/accounts/GetAccountsFeedService.java b/src/main/java/com/salessparrow/api/services/accounts/GetAccountsFeedService.java new file mode 100644 index 00000000..6a9569f3 --- /dev/null +++ b/src/main/java/com/salessparrow/api/services/accounts/GetAccountsFeedService.java @@ -0,0 +1,123 @@ +package com.salessparrow.api.services.accounts; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.salessparrow.api.domain.User; +import com.salessparrow.api.dto.entities.NextPagePayloadEntity; +import com.salessparrow.api.dto.formatter.GetAccountsFormatterDto; +import com.salessparrow.api.dto.formatter.PaginationIdentifierFormatterDto; +import com.salessparrow.api.dto.requestMapper.GetAccountsFeedDto; +import com.salessparrow.api.dto.responseMapper.GetAccountsFeedResponseDto; +import com.salessparrow.api.exception.CustomException; +import com.salessparrow.api.lib.Util; +import com.salessparrow.api.lib.crmActions.getAccounts.GetAccountsFactory; +import com.salessparrow.api.lib.errorLib.ErrorObject; +import com.salessparrow.api.lib.globalConstants.AccountConstants; + +import jakarta.servlet.http.HttpServletRequest; + +/** + * GetAccountsFeedService is a service class for the GetAccountsFeed action for the CRM. + */ +@Service +public class GetAccountsFeedService { + + @Autowired + private GetAccountsFactory getAccountsFactory; + + Logger logger = LoggerFactory.getLogger(GetAccountsFeedService.class); + + int pageNumber; + + /** + * Get accounts feed method + * @param request HttpServletRequest + * @param getAccountsFeedDto GetAccountsFeedDto + * @return GetAccountsFeedResponseDto + */ + public GetAccountsFeedResponseDto getAccountsFeed(HttpServletRequest request, + GetAccountsFeedDto getAccountsFeedDto) { + logger.info("Getting accounts feed"); + + User currentUser = (User) request.getAttribute("current_user"); + pageNumber = 1; + String searchTerm = null; + + int offset = calculateOffset(getAccountsFeedDto.getPagination_identifier()); + + return prepareResponse( + getAccountsFactory.getAccounts(currentUser, searchTerm, AccountConstants.FEED_VIEW_KIND, offset)); + } + + /** + * Calculate offset for pagination using pagination identifier + * @param paginationIdentifier String + * @return int + */ + private int calculateOffset(String paginationIdentifier) { + int offset = 0; + + if (paginationIdentifier != null) { + logger.info("Pagination identifier found"); + String decodedPaginationIdentifier = Util.base64Decode(paginationIdentifier); + PaginationIdentifierFormatterDto paginationIdentifierObj = null; + try { + ObjectMapper mapper = new ObjectMapper(); + paginationIdentifierObj = mapper.readValue(decodedPaginationIdentifier, + PaginationIdentifierFormatterDto.class); + } + catch (Exception e) { + throw new CustomException( + new ErrorObject("s_a_gafs_gaf_1", "pagination_identifier_parsing_error", e.getMessage())); + } + pageNumber = paginationIdentifierObj.getPageNumber(); + offset = (pageNumber - 1) * AccountConstants.PAGINATION_LIMIT; + } + + return offset; + } + + /** + * Preparing response + * @param accountsFactoryRes GetAccountsFormatterDto + * @return GetAccountsFeedResponseDto + */ + private GetAccountsFeedResponseDto prepareResponse(GetAccountsFormatterDto accountsFactoryRes) { + logger.info("Preparing response"); + GetAccountsFeedResponseDto accountsFeedResponse = new GetAccountsFeedResponseDto(); + + setResponseFields(accountsFeedResponse, accountsFactoryRes); + setNextPagePayload(accountsFeedResponse, pageNumber); + + return accountsFeedResponse; + } + + private void setResponseFields(GetAccountsFeedResponseDto response, GetAccountsFormatterDto factoryRes) { + response.setAccountIds(factoryRes.getAccountIds()); + response.setAccountMapById(factoryRes.getAccountMapById()); + response.setContactMapById(factoryRes.getContactMapById()); + response.setAccountContactAssociationsMapById(factoryRes.getAccountContactAssociationsMapById()); + } + + private void setNextPagePayload(GetAccountsFeedResponseDto response, int pageNumber) { + logger.info("Preparing pagination identifier"); + PaginationIdentifierFormatterDto paginationIdentifier = new PaginationIdentifierFormatterDto(); + paginationIdentifier.setPageNumber(pageNumber + 1); + + try { + ObjectMapper mapper = new ObjectMapper(); + String paginationJson = mapper.writeValueAsString(paginationIdentifier); + NextPagePayloadEntity nextPagePayload = new NextPagePayloadEntity(); + nextPagePayload.setPaginationIdentifier(Util.base64Encode(paginationJson)); + response.setNextPagePayload(nextPagePayload); + } + catch (Exception e) { + throw new CustomException(new ErrorObject("s_a_gafs_gaf_pr_1", "something_went_wrong", e.getMessage())); + } + } + +} diff --git a/src/main/java/com/salessparrow/api/services/accounts/GetNoteDetailsService.java b/src/main/java/com/salessparrow/api/services/accounts/GetNoteDetailsService.java deleted file mode 100644 index a4af37df..00000000 --- a/src/main/java/com/salessparrow/api/services/accounts/GetNoteDetailsService.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.salessparrow.api.services.accounts; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -import com.salessparrow.api.domain.User; -import com.salessparrow.api.dto.formatter.GetNoteDetailsFormatterDto; -import com.salessparrow.api.lib.crmActions.getNoteDetails.GetNoteDetailsFactory; - -import jakarta.servlet.http.HttpServletRequest; - -/** - * GetNoteDetailsService is a service class for the GetNoteDetails action for the CRM. - */ -@Service -public class GetNoteDetailsService { - @Autowired - private GetNoteDetailsFactory getNoteDetailsFactory; - - /** - * Get the details of a note - * - * @param accountId - * @param noteId - * - * @return GetNoteDetailsFormatterDto - */ - public GetNoteDetailsFormatterDto getNoteDetails(HttpServletRequest request, String noteId) { - User currentUser = (User) request.getAttribute("current_user"); - - return getNoteDetailsFactory.getNoteDetails(currentUser, noteId); - } -} diff --git a/src/main/java/com/salessparrow/api/services/accounts/GetNotesListService.java b/src/main/java/com/salessparrow/api/services/accounts/GetNotesListService.java deleted file mode 100644 index 2d59f3bd..00000000 --- a/src/main/java/com/salessparrow/api/services/accounts/GetNotesListService.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.salessparrow.api.services.accounts; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -import jakarta.servlet.http.HttpServletRequest; - -import com.salessparrow.api.domain.User; -import com.salessparrow.api.dto.formatter.GetNotesListFormatterDto; -import com.salessparrow.api.lib.crmActions.getNotesList.GetNoteListFactory; - -/** - * GetNotesListService is a service class for the GetNotesList action for the CRM. - */ -@Service -public class GetNotesListService { - @Autowired - private GetNoteListFactory getNotesListFactory; - - /** - * Get the list of notes for a given account - * @param accountId - * @param request - * - * @return GetNotesListFormatterDto - **/ - public GetNotesListFormatterDto getNotesList(HttpServletRequest request, String accountId) { - - User currentUser = (User) request.getAttribute("current_user"); - - return getNotesListFactory.getNotesList(currentUser, accountId); - } - - -} diff --git a/src/main/java/com/salessparrow/api/services/auth/DisconnectUserService.java b/src/main/java/com/salessparrow/api/services/auth/DisconnectUserService.java new file mode 100644 index 00000000..996d3754 --- /dev/null +++ b/src/main/java/com/salessparrow/api/services/auth/DisconnectUserService.java @@ -0,0 +1,30 @@ +package com.salessparrow.api.services.auth; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import com.salessparrow.api.domain.User; +import com.salessparrow.api.lib.crmActions.disconnectUser.DisconnectUserFactory; + +import jakarta.servlet.http.HttpServletRequest; + +/** + * Service for disconnecting a user from the CRM. + */ +@Service +public class DisconnectUserService { + + @Autowired + private DisconnectUserFactory disconnectUserFactory; + + /** + * Disconnect a user from the CRM. + * @param request + * @return void + */ + public void disconnect(HttpServletRequest request) { + User currentUser = (User) request.getAttribute("current_user"); + disconnectUserFactory.disconnect(currentUser); + } + +} diff --git a/src/main/java/com/salessparrow/api/services/crmOrganizationUsers/GetCrmOrganizationUsersList.java b/src/main/java/com/salessparrow/api/services/crmOrganizationUsers/GetCrmOrganizationUsersList.java new file mode 100644 index 00000000..618d3f9f --- /dev/null +++ b/src/main/java/com/salessparrow/api/services/crmOrganizationUsers/GetCrmOrganizationUsersList.java @@ -0,0 +1,57 @@ +package com.salessparrow.api.services.crmOrganizationUsers; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import com.salessparrow.api.domain.User; +import com.salessparrow.api.dto.formatter.GetCrmOrganizationUsersFormatterDto; +import com.salessparrow.api.dto.requestMapper.GetCrmOrganizationUsersDto; +import com.salessparrow.api.lib.crmActions.getCrmOrganizationUsers.GetCrmOrganizationUsersFactory; +import jakarta.servlet.http.HttpServletRequest; + +/** + * GetCrmOrganizationUsersList class for the getCrmOrganizationUsers action. + * + * + */ +@Service +public class GetCrmOrganizationUsersList { + + Logger logger = LoggerFactory.getLogger(GetCrmOrganizationUsersList.class); + + @Autowired + private GetCrmOrganizationUsersFactory getCrmOrganizationUsersFactory; + + /** + * getCrmOrganizationUsers method for the getCrmOrganizationUsers action. + * @param request + * @param crmOrganizationUsersDto + * @return GetCrmOrganizationUsersFormatterDto + */ + public GetCrmOrganizationUsersFormatterDto getCrmOrganizationUsers(HttpServletRequest request, + GetCrmOrganizationUsersDto crmOrganizationUsersDto) { + logger.info("Inside Search crm organization user service"); + User currentUser = (User) request.getAttribute("current_user"); + String formattedSearchString = ""; + if (crmOrganizationUsersDto.getQ() != null) { + formattedSearchString = formatSearchString(crmOrganizationUsersDto.getQ()); + } + + return getCrmOrganizationUsersFactory.getCrmOrganizationUsers(currentUser, formattedSearchString); + } + + /** + * formatSearchString method for the getCrmOrganizationUsers action. + * @param searchTerm + * @return String + */ + private String formatSearchString(String searchTerm) { + logger.info("format and sanitize search term"); + searchTerm = searchTerm.trim(); + + return searchTerm; + } + +} diff --git a/src/main/java/com/salessparrow/api/services/salesforce/AuthService.java b/src/main/java/com/salessparrow/api/services/salesforce/AuthService.java index 67e7ab29..44839bab 100644 --- a/src/main/java/com/salessparrow/api/services/salesforce/AuthService.java +++ b/src/main/java/com/salessparrow/api/services/salesforce/AuthService.java @@ -25,284 +25,310 @@ import com.salessparrow.api.lib.salesforce.dto.SalesforceGetIdentityDto; import com.salessparrow.api.lib.salesforce.dto.SalesforceGetTokensDto; import com.salessparrow.api.lib.salesforce.wrappers.SalesforceGetIdentity; -import com.salessparrow.api.lib.salesforce.wrappers.SalesforceGetTokens; +import com.salessparrow.api.lib.salesforce.wrappers.SalesforceTokens; import com.salessparrow.api.repositories.SalesforceOauthTokenRepository; import com.salessparrow.api.repositories.SalesforceOrganizationRepository; import com.salessparrow.api.repositories.SalesforceUserRepository; +import jakarta.servlet.http.HttpServletRequest; @Service public class AuthService { - private String code; - private String redirectUri; + private String reqApiSource; - private SalesforceGetTokensDto tokensData; - private SalesforceGetIdentityDto userData; - private SalesforceOauthToken salesforceOauthToken; - private SalesforceUser salesforceUser; - private String decryptedSalt; - private Boolean isNewUser; + private String code; - Logger logger = LoggerFactory.getLogger(AuthService.class); + private String redirectUri; - @Autowired - private AwsKms awsKms; + private SalesforceGetTokensDto tokensData; - @Autowired - private Util util; + private SalesforceGetIdentityDto userData; - @Autowired - private SalesforceOauthTokenRepository salesforceOauthTokenRepository; + private SalesforceOauthToken salesforceOauthToken; - @Autowired - private SalesforceUserRepository salesforceUserRepository; + private SalesforceUser salesforceUser; - @Autowired - private SalesforceOrganizationRepository salesforceOrganizationRepository; + private String decryptedSalt; - @Autowired - private LocalCipher localCipher; + private Boolean isNewUser; - @Autowired - private CookieHelper cookieHelper; + Logger logger = LoggerFactory.getLogger(AuthService.class); - @Autowired - private SalesforceGetTokens salesforceGetTokens; + @Autowired + private AwsKms awsKms; - @Autowired - private SalesforceGetIdentity salesforceGetIdentity; + @Autowired + private Util util; - /** - * Connect to Salesforce and create user if not exists. - * - * @param params - * - * @return AuthServiceDto - */ - public AuthServiceDto connectToSalesforce(SalesforceConnectDto params) { - this.isNewUser = true; // setting default value true to this variable, this will be updated based on conditions in further processing + @Autowired + private SalesforceUserRepository salesforceUserRepository; - code = params.getCode(); - redirectUri = params.getRedirect_uri(); + @Autowired + private SalesforceOauthTokenRepository salesforceOauthTokenRepository; - fetchOauthTokensFromSalesforce(); + @Autowired + private SalesforceOrganizationRepository salesforceOrganizationRepository; - validateAndUpsertSalesforceOrganization(); + @Autowired + private LocalCipher localCipher; - upsertSalesforceOAuthTokens(); + @Autowired + private CookieHelper cookieHelper; - verifyExistingSalesforceUser(); + @Autowired + private SalesforceTokens salesforceTokens; - if (this.isNewUser) { - fetchUserInfoFromSalesforce(); - createSalesforceUser(); - } + @Autowired + private SalesforceGetIdentity salesforceGetIdentity; + + /** + * Connect to Salesforce and create user if not exists. + * @param params + * @param request + * @return AuthServiceDto + */ + public AuthServiceDto connectToSalesforce(SalesforceConnectDto params, HttpServletRequest request) { + this.reqApiSource = (String) request.getAttribute("api_source"); + + this.isNewUser = true; // setting default value true to this variable, this will + // be updated based on conditions in further processing + + code = params.getCode(); + redirectUri = params.getRedirect_uri(); + + fetchOauthTokensFromSalesforce(); + + validateAndSaveSalesforceOrganization(); + + upsertSalesforceOAuthTokens(); + + verifyExistingSalesforceUser(); + + if (this.isNewUser) { + fetchUserInfoFromSalesforce(); + createSalesforceUser(); + } + + return prepareResponse(); + } + + /** + * Call Salesforce oauth token endpoint and fetch tokens. + * @return void + */ + private void fetchOauthTokensFromSalesforce() { + logger.info("Fetching OAuth Tokens from Salesforce"); + + HttpResponse response = salesforceTokens.getTokens(this.code, this.redirectUri); + + JsonNode jsonNode = util.getJsonNode(response.getResponseBody()); + + ObjectMapper mapper = new ObjectMapper(); + mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); + SalesforceGetTokensDto salesforceGetTokensDto = mapper.convertValue(jsonNode, SalesforceGetTokensDto.class); + + this.tokensData = salesforceGetTokensDto; + } + + /** + * Validate and save Salesforce Organization in DB. + * @return void + */ + private void validateAndSaveSalesforceOrganization() { + logger.info("Validating Salesforce Organization"); + + String salesforceOrganizationId = this.tokensData.getSalesforceOrganizationId(); + + SalesforceOrganization existingOrganizationData = salesforceOrganizationRepository + .getSalesforceOrganizationByExternalOrganizationId(salesforceOrganizationId); + + if (existingOrganizationData != null) { + logger.info("Salesforce Organization already exists"); + + if (existingOrganizationData.getStatus() != SalesforceOrganization.Status.ACTIVE) { + logger.info("Salesforce Organization is not active."); + throw new CustomException(new ErrorObject("s_s_as_vauso_1", "forbidden_api_request", + "Salesforce Organization is not active.")); + } + return; + } + + logger.info("Creating Salesforce Organization in DB"); + SalesforceOrganization salesforceOrganization = new SalesforceOrganization(); + salesforceOrganization.setExternalOrganizationId(salesforceOrganizationId); + salesforceOrganizationRepository.createSalesforceOrganization(salesforceOrganization); + } + + /** + * Upsert Salesforce Oauth Token in DB. + * @return void + */ + private void upsertSalesforceOAuthTokens() { + + logger.info("Upserting Salesforce OAuth Tokens in DB"); + + long currentTime = System.currentTimeMillis(); + String encryptedAccessToken = awsKms.encryptToken(this.tokensData.getAccessToken()); + logger.info("Time in ms for encryption : " + (System.currentTimeMillis() - currentTime)); + + String encryptedRefreshToken = awsKms.encryptToken(this.tokensData.getRefreshToken()); + String salesforceUserId = this.tokensData.getSalesforceUserId(); + + SalesforceOauthToken existingSalesforceOauthToken = salesforceOauthTokenRepository + .getSalesforceOauthTokenByExternalUserId(salesforceUserId); + + SalesforceOauthToken salesforceOauthToken = new SalesforceOauthToken(); + salesforceOauthToken.setExternalUserId(salesforceUserId); + salesforceOauthToken.setIdentityUrl(this.tokensData.getId()); + salesforceOauthToken.setAccessToken(encryptedAccessToken); + salesforceOauthToken.setRefreshToken(encryptedRefreshToken); + salesforceOauthToken.setSignature(this.tokensData.getSignature()); + salesforceOauthToken.setIdToken(this.tokensData.getIdToken()); + salesforceOauthToken.setInstanceUrl(this.tokensData.getInstanceUrl()); + salesforceOauthToken.setIssuedAt(Long.parseLong(this.tokensData.getIssuedAt())); + + if (existingSalesforceOauthToken != null) { + logger.info("Salesforce OAuth Token already exists"); + this.salesforceOauthToken = salesforceOauthTokenRepository.updateSalesforceOauthToken(salesforceOauthToken); + } + else { + logger.info("Salesforce OAuth Token does not exists. Creating new one."); + this.salesforceOauthToken = salesforceOauthTokenRepository.createSalesforceOauthToken(salesforceOauthToken); + } + + } + + /** + * Verify if Salesforce User already exists. + * @return void + */ + private void verifyExistingSalesforceUser() { + String salesforceUserId = this.tokensData.getSalesforceUserId(); + SalesforceUser salesforceUser = salesforceUserRepository.getSalesforceUserByExternalUserId(salesforceUserId); + + if (salesforceUser != null) { + logger.info("Salesforce User already exists"); + this.salesforceUser = salesforceUser; + + if (salesforceUser.getStatus() == SalesforceUser.Status.ACTIVE) { + logger.info("Salesforce User is active"); + this.isNewUser = false; + this.decryptedSalt = localCipher.decrypt(CoreConstants.encryptionKey(), + salesforceUser.getEncryptionSalt()); + } + } + } + + /** + * Fetch user info from Salesforce Identity API. + * @return void + */ + private void fetchUserInfoFromSalesforce() { + + logger.info("Calling Salesforce Identity API"); + + HttpResponse response = salesforceGetIdentity.getUserIdentity(salesforceOauthToken.getInstanceUrl(), + this.tokensData.getAccessToken()); + JsonNode jsonNode = util.getJsonNode(response.getResponseBody()); + ObjectMapper mapper = new ObjectMapper(); + mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); + SalesforceGetIdentityDto salesforceGetIdentityDto = mapper.convertValue(jsonNode, + SalesforceGetIdentityDto.class); + + this.userData = salesforceGetIdentityDto; + } + + /** + * Create Salesforce User in DB. + * @return void + */ + private void createSalesforceUser() { + + String decryptedSalt = localCipher.generateRandomSalt(); + String cookieToken = localCipher.generateRandomIv(32); + + String encryptedSalt = localCipher.encrypt(CoreConstants.encryptionKey(), decryptedSalt); + String encryptedCookieToken = localCipher.encrypt(decryptedSalt, cookieToken); + + if (this.salesforceUser != null && this.salesforceUser.getStatus() == SalesforceUser.Status.DELETED) { + logger.info("Updating Salesforce User"); + this.salesforceUser.setIdentityUrl(this.userData.getSub()); + this.salesforceUser.setExternalOrganizationId(this.userData.getOrganizationId()); + this.salesforceUser.setName(this.userData.getName()); + this.salesforceUser.setEmail(this.userData.getEmail()); + this.salesforceUser.setUserKind(UserConstants.SALESFORCE_USER_KIND); + this.salesforceUser.setCookieToken(encryptedCookieToken); + this.salesforceUser.setEncryptionSalt(encryptedSalt); + this.salesforceUser.setStatus(SalesforceUser.Status.ACTIVE); + + this.salesforceUser = salesforceUserRepository.updateSalesforceUser(this.salesforceUser); + this.decryptedSalt = decryptedSalt; + return; + } + + logger.info("Creating Salesforce User"); + SalesforceUser salesforceUser = new SalesforceUser(); + salesforceUser.setExternalUserId(this.userData.getUserId()); + salesforceUser.setIdentityUrl(this.userData.getSub()); + salesforceUser.setExternalOrganizationId(this.userData.getOrganizationId()); + salesforceUser.setName(this.userData.getName()); + salesforceUser.setEmail(this.userData.getEmail()); + salesforceUser.setUserKind(UserConstants.SALESFORCE_USER_KIND); + salesforceUser.setCookieToken(encryptedCookieToken); + salesforceUser.setEncryptionSalt(encryptedSalt); + + this.salesforceUser = salesforceUserRepository.createSalesforceUser(salesforceUser); + this.decryptedSalt = decryptedSalt; + } + + /** + * Prepare service response. + * @return AuthServiceDto + */ + private AuthServiceDto prepareResponse() { + logger.info("Preparing response"); + AuthServiceDto authServiceDto = new AuthServiceDto(); + + CurrentUserEntityDto currentUserEntityDto = new CurrentUserEntityDto(); + currentUserEntityDto.setId(this.salesforceUser.getId(this.salesforceUser.getExternalUserId())); + currentUserEntityDto.setName(this.salesforceUser.getName()); + currentUserEntityDto.setEmail(this.salesforceUser.getEmail()); + + String userLoginCookieValue = cookieHelper.getCookieValue(this.salesforceUser, + UserConstants.SALESFORCE_USER_KIND, this.decryptedSalt, reqApiSource); + + authServiceDto.setCurrentUser(currentUserEntityDto); + authServiceDto.setCurrentUserLoginCookie(userLoginCookieValue); + return authServiceDto; + } + + /** + * DTO for AuthService. + * + * @return AuthServiceDto + */ + public class AuthServiceDto { + + private CurrentUserEntityDto currentUser; + + private String currentUserLoginCookie; + + public CurrentUserEntityDto getCurrentUser() { + return currentUser; + } + + public void setCurrentUser(CurrentUserEntityDto currentUser) { + this.currentUser = currentUser; + } + + public String getCurrentUserLoginCookie() { + return currentUserLoginCookie; + } + + public void setCurrentUserLoginCookie(String currentUserLoginCookie) { + this.currentUserLoginCookie = currentUserLoginCookie; + } + + } - return prepareResponse(); - } - - /** - * Call Salesforce oauth token endpoint and fetch tokens. - * - * @return void - */ - private void fetchOauthTokensFromSalesforce() { - logger.info("Fetching OAuth Tokens from Salesforce"); - - HttpResponse response = salesforceGetTokens.getTokens(this.code, this.redirectUri); - - JsonNode jsonNode = util.getJsonNode(response.getResponseBody()); - - ObjectMapper mapper = new ObjectMapper(); - mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); - SalesforceGetTokensDto salesforceGetTokensDto = mapper.convertValue(jsonNode, - SalesforceGetTokensDto.class); - - this.tokensData = salesforceGetTokensDto; - } - - /** - * Validate and upsert Salesforce Organization in DB. - * - * @return void - */ - private void validateAndUpsertSalesforceOrganization() { - logger.info("Validating Salesforce Organization"); - - String salesforceOrganizationId = this.tokensData.getSalesforceOrganizationId(); - - SalesforceOrganization existingOrganizationData = salesforceOrganizationRepository - .getSalesforceOrganizationByExternalOrganizationId(salesforceOrganizationId); - - if (existingOrganizationData != null) { - logger.info("Salesforce Organization already exists"); - - if (existingOrganizationData.getStatus() != SalesforceOrganization.Status.ACTIVE) { - logger.info("Salesforce Organization is not active."); - throw new CustomException( - new ErrorObject( - "s_s_as_vauso_1", - "forbidden_api_request", - "Salesforce Organization is not active.")); - } - } - - logger.info("Upserting Salesforce Organization in DB"); - SalesforceOrganization salesforceOrganization = new SalesforceOrganization(); - salesforceOrganization.setExternalOrganizationId(salesforceOrganizationId); - salesforceOrganization.setStatus(SalesforceOrganization.Status.ACTIVE); - - salesforceOrganizationRepository - .saveSalesforceOrganization(salesforceOrganization); - } - - /** - * Upsert Salesforce Oauth Token in DB. - * - * @return void - */ - private void upsertSalesforceOAuthTokens() { - - logger.info("Upserting Salesforce OAuth Tokens in DB"); - - long currentTime = System.currentTimeMillis(); - String encryptedAccessToken = awsKms.encryptToken(this.tokensData.getAccessToken()); - - logger.info("Time in ms for encryption : " + (System.currentTimeMillis() - currentTime)); - - String encryptedRefreshToken = awsKms.encryptToken(this.tokensData.getRefreshToken()); - String salesforceUserId = this.tokensData.getSalesforceUserId(); - - SalesforceOauthToken salesforceOauthToken = new SalesforceOauthToken(); - // Todo: use salesforceconnect res entity object - salesforceOauthToken.setExternalUserId(salesforceUserId); - salesforceOauthToken.setIdentityUrl(this.tokensData.getId()); - salesforceOauthToken.setAccessToken(encryptedAccessToken); - salesforceOauthToken.setRefreshToken(encryptedRefreshToken); - salesforceOauthToken.setSignature(this.tokensData.getSignature()); - salesforceOauthToken.setIdToken(this.tokensData.getIdToken()); - salesforceOauthToken.setInstanceUrl(this.tokensData.getInstanceUrl()); - salesforceOauthToken.setStatus(SalesforceOauthToken.Status.ACTIVE); - salesforceOauthToken.setIssuedAt(Long.parseLong(this.tokensData.getIssuedAt())); - - this.salesforceOauthToken = salesforceOauthTokenRepository - .saveSalesforceOauthToken(salesforceOauthToken); - } - - /** - * Verify if Salesforce User already exists. - * - * @return void - */ - private void verifyExistingSalesforceUser() { - String salesforceUserId = this.tokensData.getSalesforceUserId(); - SalesforceUser salesforceUser = salesforceUserRepository.getSalesforceUserByExternalUserId(salesforceUserId); - - if (salesforceUser != null) { - logger.info("Salesforce User already exists"); - this.salesforceUser = salesforceUser; - this.isNewUser = false; - this.decryptedSalt = localCipher.decrypt(CoreConstants.encryptionKey(), salesforceUser.getEncryptionSalt()); - } - } - - /** - * Fetch user info from Salesforce Identity API. - * - * @return void - */ - private void fetchUserInfoFromSalesforce() { - - logger.info("Calling Salesforce Identity API"); - - HttpResponse response = salesforceGetIdentity.getUserIdentity(salesforceOauthToken.getInstanceUrl(), - this.tokensData.getAccessToken()); - JsonNode jsonNode = util.getJsonNode(response.getResponseBody()); - ObjectMapper mapper = new ObjectMapper(); - mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); - SalesforceGetIdentityDto salesforceGetIdentityDto = mapper.convertValue(jsonNode, - SalesforceGetIdentityDto.class); - - this.userData = salesforceGetIdentityDto; - } - - /** - * Create Salesforce User in DB. - * - * @return void - */ - private void createSalesforceUser() { - logger.info("Creating Salesforce User"); - - String decryptedSalt = localCipher.generateRandomSalt(); - String cookieToken = localCipher.generateRandomIv(32); - - String encryptedSalt = localCipher.encrypt(CoreConstants.encryptionKey(), decryptedSalt); - String encryptedCookieToken = localCipher.encrypt(decryptedSalt, cookieToken); - - SalesforceUser salesforceUser = new SalesforceUser(); - salesforceUser.setExternalUserId(this.userData.getUserId()); - salesforceUser.setIdentityUrl(this.userData.getSub()); - salesforceUser.setExternalOrganizationId(this.userData.getOrganizationId()); - salesforceUser.setName(this.userData.getName()); - salesforceUser.setEmail(this.userData.getEmail()); - salesforceUser.setUserKind(UserConstants.SALESFORCE_USER_KIND); - salesforceUser.setCookieToken(encryptedCookieToken); - salesforceUser.setEncryptionSalt(encryptedSalt); - salesforceUser.setStatus(SalesforceUser.Status.ACTIVE); - - this.salesforceUser = salesforceUserRepository.saveSalesforceUser(salesforceUser); - this.decryptedSalt = decryptedSalt; - } - - /** - * Prepare service response. - * - * @return AuthServiceDto - */ - private AuthServiceDto prepareResponse() { - logger.info("Preparing response"); - AuthServiceDto authServiceDto = new AuthServiceDto(); - - CurrentUserEntityDto currentUserEntityDto = new CurrentUserEntityDto(); - currentUserEntityDto.setId(this.salesforceUser.getId(this.salesforceUser.getExternalUserId())); - currentUserEntityDto.setName(this.salesforceUser.getName()); - currentUserEntityDto.setEmail(this.salesforceUser.getEmail()); - - String userLoginCookieValue = cookieHelper.getCookieValue(this.salesforceUser, - UserConstants.SALESFORCE_USER_KIND, - this.decryptedSalt); - - authServiceDto.setCurrentUser(currentUserEntityDto); - authServiceDto.setCurrentUserLoginCookie(userLoginCookieValue); - return authServiceDto; - } - - /** - * DTO for AuthService. - * - * @return AuthServiceDto - */ - public class AuthServiceDto { - - private CurrentUserEntityDto currentUser; - private String currentUserLoginCookie; - - public CurrentUserEntityDto getCurrentUser() { - return currentUser; - } - - public void setCurrentUser(CurrentUserEntityDto currentUser) { - this.currentUser = currentUser; - } - - public String getCurrentUserLoginCookie() { - return currentUserLoginCookie; - } - - public void setCurrentUserLoginCookie(String currentUserLoginCookie) { - this.currentUserLoginCookie = currentUserLoginCookie; - } - - } } \ No newline at end of file diff --git a/src/main/java/com/salessparrow/api/services/salesforce/RedirectUrlService.java b/src/main/java/com/salessparrow/api/services/salesforce/RedirectUrlService.java index 8866f743..844dc89e 100644 --- a/src/main/java/com/salessparrow/api/services/salesforce/RedirectUrlService.java +++ b/src/main/java/com/salessparrow/api/services/salesforce/RedirectUrlService.java @@ -16,36 +16,35 @@ @Service public class RedirectUrlService { - @Autowired - private SalesforceConstants salesforceConstants; + @Autowired + private SalesforceConstants salesforceConstants; - Logger logger = LoggerFactory.getLogger(RedirectUrlService.class); + Logger logger = LoggerFactory.getLogger(RedirectUrlService.class); - /** - * Get the redirect url for the oauth2 flow - * @param salesforceRedirectUrlDto - * - * @return RedirectUrlFormatterDto - **/ - public RedirectUrlFormatterDto getSalesforceOauthUrl(SalesforceRedirectUrlDto salesforceRedirectUrlDto) { + /** + * Get the redirect url for the oauth2 flow + * @param salesforceRedirectUrlDto + * @return RedirectUrlFormatterDto + **/ + public RedirectUrlFormatterDto getSalesforceOauthUrl(SalesforceRedirectUrlDto salesforceRedirectUrlDto) { - String redirectUri = salesforceRedirectUrlDto.getRedirect_uri(); - String state = salesforceRedirectUrlDto.getState(); + String redirectUri = salesforceRedirectUrlDto.getRedirect_uri(); + String state = salesforceRedirectUrlDto.getState(); - String salesforceLoginUrl = salesforceConstants.oauth2AuthorizeUrl(); - String salesforceClientId = CoreConstants.salesforceClientId(); + String salesforceLoginUrl = salesforceConstants.oauth2AuthorizeUrl(); + String salesforceClientId = CoreConstants.salesforceClientId(); - String salesforceOauthUrl = salesforceLoginUrl + "?response_type=code" + - "&client_id=" + salesforceClientId + - "&redirect_uri=" + redirectUri; + String salesforceOauthUrl = salesforceLoginUrl + "?response_type=code" + "&client_id=" + salesforceClientId + + "&redirect_uri=" + redirectUri; - if (state != null && !state.isEmpty()) { - salesforceOauthUrl += "&state=" + state; - } + if (state != null && !state.isEmpty()) { + salesforceOauthUrl += "&state=" + state; + } - RedirectUrlFormatterDto redirectUrlFormatterDto = new RedirectUrlFormatterDto(); - redirectUrlFormatterDto.setUrl(salesforceOauthUrl); + RedirectUrlFormatterDto redirectUrlFormatterDto = new RedirectUrlFormatterDto(); + redirectUrlFormatterDto.setUrl(salesforceOauthUrl); + + return redirectUrlFormatterDto; + } - return redirectUrlFormatterDto; - } } \ No newline at end of file diff --git a/src/main/java/com/salessparrow/api/services/suggestions/CrmActionsSuggestionsService.java b/src/main/java/com/salessparrow/api/services/suggestions/CrmActionsSuggestionsService.java new file mode 100644 index 00000000..2923e2e2 --- /dev/null +++ b/src/main/java/com/salessparrow/api/services/suggestions/CrmActionsSuggestionsService.java @@ -0,0 +1,30 @@ +package com.salessparrow.api.services.suggestions; + +import org.slf4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import com.salessparrow.api.controllers.SuggestionsController; +import com.salessparrow.api.dto.formatter.CrmActionSuggestionsFormatterDto; +import com.salessparrow.api.dto.requestMapper.CrmActionsSuggestionsDto; +import com.salessparrow.api.lib.GetCrmActionSuggestions; + +/** + * CrmActionsSuggestionsService is a service class for the crm actions suggestions. + */ +@Service +public class CrmActionsSuggestionsService { + + @Autowired + private GetCrmActionSuggestions getCrmActionSuggestions; + + private Logger logger = org.slf4j.LoggerFactory.getLogger(SuggestionsController.class); + + public CrmActionSuggestionsFormatterDto getSuggestions(CrmActionsSuggestionsDto crmActionsSuggestionsDto) { + logger.info("Crm actions service called"); + String text = crmActionsSuggestionsDto.getText(); + + return getCrmActionSuggestions.getTaskSuggestions(text); + } + +} diff --git a/src/main/java/com/salessparrow/api/services/users/GetCurrentUserService.java b/src/main/java/com/salessparrow/api/services/users/GetCurrentUserService.java index 3b4bf580..6245d3cc 100644 --- a/src/main/java/com/salessparrow/api/services/users/GetCurrentUserService.java +++ b/src/main/java/com/salessparrow/api/services/users/GetCurrentUserService.java @@ -13,14 +13,15 @@ @Service public class GetCurrentUserService { - public CurrentUserEntityDto getCurrentUser(HttpServletRequest request) { - User currentUser = (User) request.getAttribute("current_user"); + public CurrentUserEntityDto getCurrentUser(HttpServletRequest request) { + User currentUser = (User) request.getAttribute("current_user"); - CurrentUserEntityDto currentUserEntityDto = new CurrentUserEntityDto(); - currentUserEntityDto.setId(currentUser.getExternalUserId()); - currentUserEntityDto.setName(currentUser.getName()); - currentUserEntityDto.setEmail(currentUser.getEmail()); + CurrentUserEntityDto currentUserEntityDto = new CurrentUserEntityDto(); + currentUserEntityDto.setId(currentUser.getExternalUserId()); + currentUserEntityDto.setName(currentUser.getName()); + currentUserEntityDto.setEmail(currentUser.getEmail()); + + return currentUserEntityDto; + } - return currentUserEntityDto; - } } \ No newline at end of file diff --git a/src/main/java/com/salessparrow/api/utility/CacheKeyGenerator.java b/src/main/java/com/salessparrow/api/utility/CacheKeyGenerator.java new file mode 100644 index 00000000..73426574 --- /dev/null +++ b/src/main/java/com/salessparrow/api/utility/CacheKeyGenerator.java @@ -0,0 +1,36 @@ +package com.salessparrow.api.utility; + +import com.salessparrow.api.config.CoreConstants; +import org.springframework.cache.interceptor.KeyGenerator; +import java.lang.reflect.Method; + +/** + * Cache Key Generator + */ +public class CacheKeyGenerator implements KeyGenerator { + + /** + * Generate the key based on the method name and the parameters. Append the + * environment name to the key. + * @param target + * @param method + * @param params + * @return String + */ + @Override + public Object generate(Object target, Method method, Object... params) { + String prefix = CoreConstants.environment(); + StringBuilder key = new StringBuilder(); + key.append(prefix); + key.append(":"); + key.append(target.getClass().getSimpleName()); + key.append(":"); + key.append(method.getName()); + for (Object param : params) { + key.append(":"); + key.append(param.toString()); + } + return key.toString(); + }; + +} diff --git a/src/main/java/com/salessparrow/api/utility/Memcached.java b/src/main/java/com/salessparrow/api/utility/Memcached.java index bede6bec..eb3ee843 100644 --- a/src/main/java/com/salessparrow/api/utility/Memcached.java +++ b/src/main/java/com/salessparrow/api/utility/Memcached.java @@ -12,137 +12,129 @@ */ public class Memcached implements Cache { - private static final Logger logger = LoggerFactory.getLogger(Memcached.class); - - private String name; - - private MemcachedClient cache; - - private int expiration; - - /** - * Constructor - * - * @param name - * @param expiration - */ - public Memcached(String name, int expiration, MemcachedClient memcachedClient) { - this.name = name; - this.expiration = expiration; - this.cache = memcachedClient; - } - - /** - * Returns the name of the cache. - * - * @param name - */ - @Override - public String getName() { - return name; - } - - /** - * Returns the native cache. - * - * @param name - */ - @Override - public Object getNativeCache() { - return cache; - } - - /** - * Returns the value associated with the key. - * - * @param key - */ - @Override - public ValueWrapper get(final Object key) { - String keyString = name + "_" + key.toString(); - Object value = null; - try { - value = cache.get(keyString); - } catch (final Exception e) { - logger.warn(e.getMessage()); - } - if (value == null) { - logger.debug("cache miss for key: " + keyString); - return null; - } - logger.debug("cache hit for key: " + keyString); - - return new SimpleValueWrapper(value); - } - - /** - * Associates the value with the key. - * - * @param key - * @param value - */ - @Override - public void put(final Object key, final Object value) { - String keyString = name + "_" + key.toString(); - if (value != null) { - cache.set(keyString, expiration, value); - logger.debug("cache put for key: " + keyString); - } - } - - /** - * Removes the key and its associated value. - * - * @param key - */ - @Override - public void evict(final Object key) { - String keyString = name + "_" + key.toString(); - this.cache.delete(keyString); - logger.debug("cache delete for key: " + keyString); - } - - /** - * Clears the cache. - * - * @param key - */ - @Override - public void clear() { - cache.flush(); - logger.debug("cache clear completed"); - } - - /** - * Returns the value associated with the key. - * - * @param key - * @param aClass - */ - @Override - public T get(Object o, Class aClass) { - return null; - } - - /** - * Returns the value associated with the key. - * - * @param key - * @param callable - */ - @Override - public T get(Object o, Callable callable) { - return null; - } - - /** - * Associates the value with the key if absent. - * - * @param key - * @param value - */ - @Override - public ValueWrapper putIfAbsent(Object o, Object o1) { - return null; - } + private static final Logger logger = LoggerFactory.getLogger(Memcached.class); + + private String name; + + private MemcachedClient cache; + + private int expiration; + + /** + * Constructor + * @param name + * @param expiration + */ + public Memcached(String name, int expiration, MemcachedClient memcachedClient) { + this.name = name; + this.expiration = expiration; + this.cache = memcachedClient; + } + + /** + * Returns the name of the cache. + * @param name + */ + @Override + public String getName() { + return name; + } + + /** + * Returns the native cache. + * @param name + */ + @Override + public Object getNativeCache() { + return cache; + } + + /** + * Returns the value associated with the key. + * @param key + */ + @Override + public ValueWrapper get(final Object key) { + String keyString = name + "_" + key.toString(); + Object value = null; + try { + value = cache.get(keyString); + } + catch (final Exception e) { + logger.warn(e.getMessage()); + } + if (value == null) { + logger.debug("cache miss for key: " + keyString); + return null; + } + logger.debug("cache hit for key: " + keyString); + + return new SimpleValueWrapper(value); + } + + /** + * Associates the value with the key. + * @param key + * @param value + */ + @Override + public void put(final Object key, final Object value) { + String keyString = name + "_" + key.toString(); + if (value != null) { + cache.set(keyString, expiration, value); + logger.debug("cache put for key: " + keyString); + } + } + + /** + * Removes the key and its associated value. + * @param key + */ + @Override + public void evict(final Object key) { + String keyString = name + "_" + key.toString(); + this.cache.delete(keyString); + logger.debug("cache delete for key: " + keyString); + } + + /** + * Clears the cache. + * @param key + */ + @Override + public void clear() { + cache.flush(); + logger.debug("cache clear completed"); + } + + /** + * Returns the value associated with the key. + * @param key + * @param aClass + */ + @Override + public T get(Object o, Class aClass) { + return null; + } + + /** + * Returns the value associated with the key. + * @param key + * @param callable + */ + @Override + public T get(Object o, Callable callable) { + return null; + } + + /** + * Associates the value with the key if absent. + * @param key + * @param value + */ + @Override + public ValueWrapper putIfAbsent(Object o, Object o1) { + return null; + } + } \ No newline at end of file diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index d2821b57..d79c2895 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,11 +1,5 @@ # Server -server.port=80 - -# Dynamodb -aws.dynamodb.endpoint=${DYNAMO_DB_URL} -aws.dynamodb.accessKey=${AWS_IAM_ACCESS_KEY_ID} -aws.dynamodb.secretKey=${AWS_IAM_SECRET_ACCESS_KEY} -aws.region=${AWS_IAM_REGION} +server.port=8080 # Spring boot profile spring.profiles.active=${ENVIRONMENT} @@ -15,4 +9,6 @@ spring.mvc.throw-exception-if-no-handler-found=true spring.mvc.static-path-pattern: /static # Suppress warning like o.s.w.s.m.m.a.ExceptionHandlerExceptionResolver - Resolved [com.salessparrow.api.exception.CustomException] -logging.level.org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver=ERROR \ No newline at end of file +logging.level.org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver=ERROR +logging.level.org.springframework.security=INFO +logging.level.org.springframework.web=INFO \ No newline at end of file diff --git a/src/main/resources/config/ApiErrorConfig.json b/src/main/resources/config/ApiErrorConfig.json index d82d347c..34901185 100644 --- a/src/main/resources/config/ApiErrorConfig.json +++ b/src/main/resources/config/ApiErrorConfig.json @@ -14,6 +14,11 @@ "code": "NOT_FOUND", "message": "Resource not found. Please check if the resource identifier passed in the URL is valid and try again." }, + "unsupported_media_type": { + "httpCode": "415", + "code": "UNSUPPORTED_MEDIA_TYPE", + "message": "Unsupported Media Type. Content-Type header should be application/json." + }, "something_went_wrong": { "httpCode": "500", "code": "INTERNAL_SERVER_ERROR", @@ -39,9 +44,14 @@ "code": "INVALID_PARAMS", "message": "At least one parameter is invalid or missing." }, - "forbidden_api_request": { - "httpCode": "403", - "code": "FORBIDDEN", - "message": "Forbidden API request. You do not have the necessary permissions." - } + "forbidden_api_request": { + "httpCode": "403", + "code": "FORBIDDEN", + "message": "Forbidden API request. You do not have the necessary permissions." + }, + "pagination_identifier_parsing_error": { + "httpCode": "400", + "code": "PAGINATION_IDENTIFIER_PARSING_ERROR", + "message": "Error parsing pagination identifier." + } } \ No newline at end of file diff --git a/src/main/resources/config/ParamErrorConfig.json b/src/main/resources/config/ParamErrorConfig.json index 0b724abb..eb3fb4a1 100644 --- a/src/main/resources/config/ParamErrorConfig.json +++ b/src/main/resources/config/ParamErrorConfig.json @@ -1,17 +1,62 @@ { + "invalid_code": { + "parameter": "code", + "param_error_identifier": "invalid_code", + "message": "The code you sent is incorrect. Please double check and try again." + }, "invalid_email": { "parameter": "email", "param_error_identifier": "invalid_email", "message": "The email address you entered is incorrect. Please double check and try again." }, - "invalid_redirect_uri":{ + "invalid_redirect_uri": { "parameter": "redirect_uri", "param_error_identifier": "invalid_redirect_uri", "message": "Invalid redirect URI. Please double check and try again." }, - "search_term_too_long":{ + "search_term_too_long": { "parameter": "q", "param_error_identifier": "search_term_too_long", "message": "Search term too long. Please shorten and try again." + }, + "invalid_pagination_identifier":{ + "parameter": "pagination_identifier", + "param_error_identifier": "invalid_pagination_identifier", + "message": "Invalid pagination identifier. Please double check and try again." + }, + "invalid_search_term":{ + "parameter": "q", + "param_error_identifier": "invalid_search_term", + "message": "Invalid search term. Please try again." + }, + "invalid_due_date": { + "parameter": "due_date", + "param_error_identifier": "invalid_due_date", + "message": "Invalid or missing due date. Please double check and try again." + }, + "description_too_long": { + "parameter": "description", + "param_error_identifier": "description_too_long", + "message": "Description too long. Please shorten and try again." + }, + "invalid_task_id":{ + "parameter": "task_id", + "param_error_identifier": "invalid_task_id", + "message": "Invalid task ID. Please double check and try again." + }, + "invalid_note_id": { + "parameter": "note_id", + "param_error_identifier": "invalid_note_id", + "message": "The note id you sent is incorrect. Please double check and try again." + }, + "text_too_long": { + "parameter": "text", + "param_error_identifier": "text_too_long", + "message": "Text too long. Please shorten and try again." + }, + "invalid_account_id": { + "parameter": "account_id", + "param_error_identifier": "invalid_account_id", + "message": "The account id you sent is incorrect. Please double check and try again." } } \ No newline at end of file diff --git a/src/main/resources/logback-test.xml b/src/main/resources/logback-test.xml new file mode 100644 index 00000000..b8647966 --- /dev/null +++ b/src/main/resources/logback-test.xml @@ -0,0 +1,25 @@ + + + + + + + + %d{yyyy-MM-dd HH:mm:ss} %highlight(%-5level) [%X{trackingId}] %logger{36} - %highlight(%msg%n) + + + + + + + + + + + + + + + + + diff --git a/src/test/java/com/salessparrow/api/functional/SalesSparrowApiTest.java b/src/test/java/com/salessparrow/api/functional/SalesSparrowApiTest.java new file mode 100644 index 00000000..13014bca --- /dev/null +++ b/src/test/java/com/salessparrow/api/functional/SalesSparrowApiTest.java @@ -0,0 +1,21 @@ +package com.salessparrow.api.functional; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.ApplicationContext; + +import static org.assertj.core.api.Assertions.assertThat; + +@SpringBootTest +public class SalesSparrowApiTest { + + @Autowired + private ApplicationContext ctx; + + @Test + public void contextLoads() { + assertThat(ctx).isNotNull(); + } + +} diff --git a/src/test/java/com/salessparrow/api/functional/config/SecurityConfigTest.java b/src/test/java/com/salessparrow/api/functional/config/SecurityConfigTest.java new file mode 100644 index 00000000..701a2bcd --- /dev/null +++ b/src/test/java/com/salessparrow/api/functional/config/SecurityConfigTest.java @@ -0,0 +1,77 @@ +package com.salessparrow.api.functional.config; + +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; +import java.io.IOException; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.Import; +import org.springframework.http.MediaType; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; + +import com.github.dynamobee.exception.DynamobeeException; +import com.salessparrow.api.helper.Cleanup; +import com.salessparrow.api.helper.Common; +import com.salessparrow.api.helper.LoadFixture; +import com.salessparrow.api.helper.Setup; + +@SpringBootTest +@AutoConfigureMockMvc +@WebAppConfiguration +@Import({ Setup.class, Cleanup.class, Common.class, LoadFixture.class }) +public class SecurityConfigTest { + + @Autowired + private MockMvc mockMvc; + + @Autowired + private Setup setup; + + @Autowired + private Cleanup cleanup; + + @Autowired + private Common common; + + @BeforeEach + public void setUp() throws DynamobeeException, IOException { + setup.perform(); + } + + @AfterEach + public void tearDown() { + cleanup.perform(); + } + + @Test + public void allRoutesShouldBeAccessibleWithoutAuthenticationAndAuthorization() throws Exception { + ResultActions resultActions = mockMvc.perform(MockMvcRequestBuilders.get("/api/v1/auth/salesforce/redirect-url") + .param("redirect_uri", "http://localhost:3000") + .contentType(MediaType.APPLICATION_JSON)); + assertEquals(resultActions.andReturn().getResponse().getStatus(), 200); + } + + @Test + public void securityHeadersShouldBeSet() throws Exception { + mockMvc + .perform(MockMvcRequestBuilders.get("/api/v1/auth/salesforce/redirect-url") + .param("redirect_uri", "http://localhost:3000") + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(header().string("X-Content-Type-Options", "nosniff")) + .andExpect(header().string("X-XSS-Protection", "1; mode=block")) + .andExpect(header().string("Cache-Control", "no-cache, no-store, max-age=0, must-revalidate")) + .andExpect(header().string("Pragma", "no-cache")) + .andExpect(header().string("Expires", "0")) + .andExpect(header().string("X-Frame-Options", "DENY")) + .andExpect(header().string("Referrer-Policy", "same-origin")) + .andExpect(header().string("Content-Type", "application/json")); + } + +} diff --git a/src/test/java/com/salessparrow/api/functional/controllers/accountController/CreateNoteTest.java b/src/test/java/com/salessparrow/api/functional/controllers/accountController/CreateNoteTest.java index 05517248..43d6e7af 100644 --- a/src/test/java/com/salessparrow/api/functional/controllers/accountController/CreateNoteTest.java +++ b/src/test/java/com/salessparrow/api/functional/controllers/accountController/CreateNoteTest.java @@ -5,6 +5,7 @@ import static org.mockito.Mockito.when; import java.io.IOException; +import java.io.InputStream; import java.util.List; import java.util.stream.Stream; @@ -30,6 +31,7 @@ import com.github.dynamobee.exception.DynamobeeException; import com.salessparrow.api.helper.Cleanup; import com.salessparrow.api.helper.Common; +import com.salessparrow.api.helper.Constants; import com.salessparrow.api.helper.FixtureData; import com.salessparrow.api.helper.LoadFixture; import com.salessparrow.api.helper.Scenario; @@ -46,73 +48,92 @@ @Import({ Setup.class, Cleanup.class, Common.class, LoadFixture.class }) public class CreateNoteTest { - @Autowired - private MockMvc mockMvc; - @Autowired - private Common common; - @Autowired - private LoadFixture loadFixture; - @Autowired - private Setup setup; - @Autowired - private Cleanup cleanup; - - @MockBean - private MakeCompositeRequest makeCompositeRequestMock; - - @BeforeEach - public void setUp() throws DynamobeeException { - setup.perform(); - } - - @AfterEach - public void tearDown() { - cleanup.perform(); - } - - @ParameterizedTest - @MethodSource("testScenariosProvider") - public void createNote(Scenario testScenario) throws Exception { - - // Load fixture data - String currentFunctionName = new Object(){}.getClass().getEnclosingMethod().getName(); - FixtureData fixtureData = common.loadFixture("classpath:fixtures/controllers/accountController/createNote.fixtures.json", currentFunctionName); - loadFixture.perform(fixtureData); - - // Read data from the scenario - ObjectMapper objectMapper = new ObjectMapper(); - String cookieValue = (String) testScenario.getInput().get("cookie"); - String accountId = (String) testScenario.getInput().get("accountId"); - - // Prepare mock responses - HttpResponse createNoteMockResponse = new HttpResponse(); - createNoteMockResponse.setResponseBody(objectMapper.writeValueAsString(testScenario.getMocks().get("makeCompositeRequest"))); - when(makeCompositeRequestMock.makePostRequest(any(), any())).thenReturn(createNoteMockResponse); - - // Perform the request - String requestBody = objectMapper.writeValueAsString(testScenario.getInput().get("body")); - String url = "/api/v1/accounts/" + accountId + "/notes"; - - ResultActions resultActions = mockMvc.perform(MockMvcRequestBuilders.post(url) - .cookie(new Cookie(CookieConstants.USER_LOGIN_COOKIE_NAME, cookieValue)) - .content(requestBody) - .contentType(MediaType.APPLICATION_JSON)); - - // Check the response - String expectedOutput = objectMapper.writeValueAsString(testScenario.getOutput()); - String actualOutput = resultActions.andReturn().getResponse().getContentAsString(); - assertEquals(expectedOutput, actualOutput); - } - - static Stream testScenariosProvider() throws IOException { - List testScenarios = loadScenarios(); - return testScenarios.stream(); - } - - private static List loadScenarios() throws IOException { - String scenariosPath = "classpath:data/controllers/accountController/createNote.scenarios.json"; - Resource resource = new DefaultResourceLoader().getResource(scenariosPath); - ObjectMapper objectMapper = new ObjectMapper(); - return objectMapper.readValue(resource.getInputStream(), new TypeReference>() {}); - } + @Autowired + private MockMvc mockMvc; + + @Autowired + private Common common; + + @Autowired + private LoadFixture loadFixture; + + @Autowired + private Setup setup; + + @Autowired + private Cleanup cleanup; + + @MockBean + private MakeCompositeRequest makeCompositeRequestMock; + + @BeforeEach + public void setUp() throws DynamobeeException { + setup.perform(); + } + + @AfterEach + public void tearDown() { + cleanup.perform(); + } + + @ParameterizedTest + @MethodSource("testScenariosProvider") + public void createNote(Scenario testScenario) throws Exception { + + // Load fixture data + String currentFunctionName = new Object() { + }.getClass().getEnclosingMethod().getName(); + FixtureData fixtureData = common.loadFixture( + "classpath:fixtures/functional/controllers/accountController/createNote.fixtures.json", + currentFunctionName); + loadFixture.perform(fixtureData); + + // Read data from the scenario + ObjectMapper objectMapper = new ObjectMapper(); + String cookieValue = Constants.SALESFORCE_ACTIVE_USER_COOKIE_VALUE; + String accountId = (String) testScenario.getInput().get("accountId"); + + // Prepare mock responses + HttpResponse createNoteMockResponse = new HttpResponse(); + createNoteMockResponse + .setResponseBody(objectMapper.writeValueAsString(testScenario.getMocks().get("makeCompositeRequest"))); + when(makeCompositeRequestMock.makePostRequest(any(), any())).thenReturn(createNoteMockResponse); + + // Perform the request + String requestBody = objectMapper.writeValueAsString(testScenario.getInput().get("body")); + String url = "/api/v1/accounts/" + accountId + "/notes"; + + ResultActions resultActions = mockMvc.perform(MockMvcRequestBuilders.post(url) + .cookie(new Cookie(CookieConstants.USER_LOGIN_COOKIE_NAME, cookieValue)) + .content(requestBody) + .contentType(MediaType.APPLICATION_JSON)); + + // Check the response + String expectedOutput = objectMapper.writeValueAsString(testScenario.getOutput()); + String actualOutput = resultActions.andReturn().getResponse().getContentAsString(); + + if (resultActions.andReturn().getResponse().getStatus() == 200) { + assertEquals(expectedOutput, actualOutput); + } + else { + common.compareErrors(testScenario, actualOutput); + } + } + + static Stream testScenariosProvider() throws IOException { + List testScenarios = loadScenarios(); + return testScenarios.stream(); + } + + private static List loadScenarios() throws IOException { + String scenariosPath = "classpath:data/functional/controllers/accountController/createNote.scenarios.json"; + Resource resource = new DefaultResourceLoader().getResource(scenariosPath); + ObjectMapper objectMapper = new ObjectMapper(); + + try (InputStream inputStream = resource.getInputStream()) { + return objectMapper.readValue(resource.getInputStream(), new TypeReference>() { + }); + } + } + } diff --git a/src/test/java/com/salessparrow/api/functional/controllers/accountController/GetAccountListTest.java b/src/test/java/com/salessparrow/api/functional/controllers/accountController/GetAccountListTest.java index a472b658..b816df4e 100644 --- a/src/test/java/com/salessparrow/api/functional/controllers/accountController/GetAccountListTest.java +++ b/src/test/java/com/salessparrow/api/functional/controllers/accountController/GetAccountListTest.java @@ -5,6 +5,7 @@ import static org.mockito.Mockito.when; import java.io.IOException; +import java.io.InputStream; import java.util.List; import java.util.stream.Stream; @@ -47,78 +48,89 @@ @WebAppConfiguration @Import({ Setup.class, Cleanup.class, Common.class, LoadFixture.class }) public class GetAccountListTest { - @Autowired - private MockMvc mockMvc; - @Autowired - private Common common; - @Autowired - private LoadFixture loadFixture; - @Autowired - private Setup setup; - @Autowired - private Cleanup cleanup; - - @MockBean - private MakeCompositeRequest makeCompositeRequestMock; - - @BeforeEach - public void setUp() throws DynamobeeException { - setup.perform(); - } - - @AfterEach - public void tearDown() { - cleanup.perform(); - } - - @ParameterizedTest - @MethodSource("testScenariosProvider") - public void getAccountList(Scenario testScenario) throws Exception { - // Load fixture data - String currentFunctionName = new Object(){}.getClass().getEnclosingMethod().getName(); - FixtureData fixtureData = common.loadFixture("classpath:fixtures/controllers/accountController/getAccountList.fixtures.json", currentFunctionName); - System.out.println("fixtureData++++++ "+fixtureData); - loadFixture.perform(fixtureData); - - // Read data from the scenario - ObjectMapper objectMapper = new ObjectMapper(); - String cookieValue = Constants.SALESFORCE_ACTIVE_USET_COOKIE_VALUE; - - // Prepare mock responses - HttpResponse getAccountMockResponse = new HttpResponse(); - getAccountMockResponse.setResponseBody(objectMapper.writeValueAsString(testScenario.getMocks().get("makeCompositeRequest"))); - when(makeCompositeRequestMock.makePostRequest(any(), any())).thenReturn(getAccountMockResponse); - - // Perform the request - String url = "/api/v1/accounts"; - String q = objectMapper.writeValueAsString(testScenario.getInput().get("q")); - System.out.println("q++++++ "+q); - - ResultActions resultActions = mockMvc.perform(MockMvcRequestBuilders.get(url) - .cookie(new Cookie(CookieConstants.USER_LOGIN_COOKIE_NAME, cookieValue)) - .param("q", q) - .contentType(MediaType.APPLICATION_JSON)); - - - // Check the response - String expectedOutput = objectMapper.writeValueAsString(testScenario.getOutput()); - String actualOutput = resultActions.andReturn().getResponse().getContentAsString(); - - JsonNode expectedOutputJson = objectMapper.readTree(expectedOutput); - JsonNode actualOutputJson = objectMapper.readTree(actualOutput); - - assertEquals(expectedOutputJson, actualOutputJson); - } - - static Stream testScenariosProvider() throws IOException { - List testScenarios = loadScenarios(); - return testScenarios.stream(); - } - - private static List loadScenarios() throws IOException { - String scenariosPath = "classpath:data/controllers/accountController/getAccountList.scenarios.json"; - Resource resource = new DefaultResourceLoader().getResource(scenariosPath); - ObjectMapper objectMapper = new ObjectMapper(); - return objectMapper.readValue(resource.getInputStream(), new TypeReference>() {}); - } -} + + @Autowired + private MockMvc mockMvc; + + @Autowired + private Common common; + + @Autowired + private LoadFixture loadFixture; + + @Autowired + private Setup setup; + + @Autowired + private Cleanup cleanup; + + @MockBean + private MakeCompositeRequest makeCompositeRequestMock; + + @BeforeEach + public void setUp() throws DynamobeeException { + setup.perform(); + } + + @AfterEach + public void tearDown() { + cleanup.perform(); + } + + @ParameterizedTest + @MethodSource("testScenariosProvider") + public void getAccountList(Scenario testScenario) throws Exception { + // Load fixture data + String currentFunctionName = new Object() { + }.getClass().getEnclosingMethod().getName(); + FixtureData fixtureData = common.loadFixture( + "classpath:fixtures/functional/controllers/accountController/getAccountList.fixtures.json", + currentFunctionName); + loadFixture.perform(fixtureData); + + // Read data from the scenario + ObjectMapper objectMapper = new ObjectMapper(); + String cookieValue = Constants.SALESFORCE_ACTIVE_USER_COOKIE_VALUE; + + // Prepare mock responses + HttpResponse getAccountMockResponse = new HttpResponse(); + getAccountMockResponse + .setResponseBody(objectMapper.writeValueAsString(testScenario.getMocks().get("makeCompositeRequest"))); + when(makeCompositeRequestMock.makePostRequest(any(), any())).thenReturn(getAccountMockResponse); + + // Perform the request + String url = "/api/v1/accounts"; + String q = objectMapper.writeValueAsString(testScenario.getInput().get("q")); + + ResultActions resultActions = mockMvc.perform(MockMvcRequestBuilders.get(url) + .cookie(new Cookie(CookieConstants.USER_LOGIN_COOKIE_NAME, cookieValue)) + .param("q", q) + .contentType(MediaType.APPLICATION_JSON)); + + // Check the response + String expectedOutput = objectMapper.writeValueAsString(testScenario.getOutput()); + String actualOutput = resultActions.andReturn().getResponse().getContentAsString(); + + JsonNode expectedOutputJson = objectMapper.readTree(expectedOutput); + JsonNode actualOutputJson = objectMapper.readTree(actualOutput); + + assertEquals(expectedOutputJson, actualOutputJson); + } + + static Stream testScenariosProvider() throws IOException { + List testScenarios = loadScenarios(); + return testScenarios.stream(); + } + + private static List loadScenarios() throws IOException { + String scenariosPath = "classpath:data/functional/controllers/accountController/getAccountList.scenarios.json"; + Resource resource = new DefaultResourceLoader().getResource(scenariosPath); + ObjectMapper objectMapper = new ObjectMapper(); + + try (InputStream inputStream = resource.getInputStream()) { + return objectMapper.readValue(resource.getInputStream(), new TypeReference>() { + }); + } + } + +} \ No newline at end of file diff --git a/src/test/java/com/salessparrow/api/functional/controllers/accountController/GetAccountsFeedTest.java b/src/test/java/com/salessparrow/api/functional/controllers/accountController/GetAccountsFeedTest.java new file mode 100644 index 00000000..503669cf --- /dev/null +++ b/src/test/java/com/salessparrow/api/functional/controllers/accountController/GetAccountsFeedTest.java @@ -0,0 +1,199 @@ +package com.salessparrow.api.functional.controllers.accountController; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.io.IOException; +import java.util.List; +import java.util.stream.Stream; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; +import org.springframework.core.io.DefaultResourceLoader; +import org.springframework.core.io.Resource; +import org.springframework.http.MediaType; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.dynamobee.exception.DynamobeeException; +import com.salessparrow.api.dto.formatter.PaginationIdentifierFormatterDto; +import com.salessparrow.api.helper.Cleanup; +import com.salessparrow.api.helper.Common; +import com.salessparrow.api.helper.Constants; +import com.salessparrow.api.helper.FixtureData; +import com.salessparrow.api.helper.LoadFixture; +import com.salessparrow.api.helper.Scenario; +import com.salessparrow.api.helper.Setup; +import com.salessparrow.api.lib.globalConstants.CookieConstants; +import com.salessparrow.api.lib.httpLib.HttpClient; +import com.salessparrow.api.lib.httpLib.HttpClient.HttpResponse; +import com.salessparrow.api.lib.salesforce.helper.MakeCompositeRequest; +import com.salessparrow.api.lib.salesforce.helper.SalesforceRequestInterface; + +import jakarta.servlet.http.Cookie; + +@SpringBootTest +@AutoConfigureMockMvc +@WebAppConfiguration +@Import({ Setup.class, Cleanup.class, Common.class, LoadFixture.class }) +public class GetAccountsFeedTest { + + @Autowired + private MockMvc mockMvc; + + @Autowired + private Common common; + + @Autowired + private LoadFixture loadFixture; + + @Autowired + private Setup setup; + + @Autowired + private Cleanup cleanup; + + Logger logger = LoggerFactory.getLogger(GetAccountsFeedTest.class); + + @MockBean + private MakeCompositeRequest makeCompositeRequestMock; + + @MockBean + private SalesforceRequestInterface salesforceRequestInterfaceMock; + + @BeforeEach + public void setUp() throws DynamobeeException { + setup.perform(); + } + + @AfterEach + public void tearDown() { + cleanup.perform(); + } + + @ParameterizedTest + @MethodSource("testScenariosProvider") + public void getAccountsFeed(Scenario testScenario) throws Exception { + + logger.info("Running test scenario: " + testScenario.getDescription()); + String currentFunctionName = new Object() { + }.getClass().getEnclosingMethod().getName(); + // Load fixture data and set up mocks + loadFixtureDataAndSetUpMocks(testScenario, currentFunctionName); + + // Perform the request + ResultActions resultActions = performRequest(testScenario); + + // Check the response + checkResponse(testScenario, resultActions); + } + + private void loadFixtureDataAndSetUpMocks(Scenario testScenario, String currentFunctionName) throws Exception { + + FixtureData fixtureData = common.loadFixture( + "classpath:fixtures/controllers/accountController/getAccountsFeed.fixtures.json", currentFunctionName); + loadFixture.perform(fixtureData); + + // Read data from the scenario + ObjectMapper objectMapper = new ObjectMapper(); + + // Prepare makeCompositeRequestMock responses + if (testScenario.getMocks() != null && testScenario.getMocks().containsKey("makeCompositeRequest")) { + HttpResponse getAccountMockResponse = new HttpResponse(); + getAccountMockResponse + .setResponseBody(objectMapper.writeValueAsString(testScenario.getMocks().get("makeCompositeRequest"))); + when(makeCompositeRequestMock.makePostRequest(any(), any())).thenReturn(getAccountMockResponse); + } + + if (testScenario.getMocks() != null && testScenario.getMocks().containsKey("writeValueAsString")) { + ObjectMapper mapper = mock(ObjectMapper.class); + + String errorMessage = testScenario.getMocks().get("writeValueAsString").toString(); + when(mapper.writeValueAsString(any())).thenThrow(new RuntimeException(errorMessage)); + } + + if (testScenario.getMocks() != null && testScenario.getMocks().containsKey("readValue")) { + ObjectMapper mapper = mock(ObjectMapper.class); + + String errorMessage = testScenario.getMocks().get("readValue").toString(); + when(mapper.readValue(any(JsonParser.class), eq(PaginationIdentifierFormatterDto.class))) + .thenThrow(new RuntimeException(errorMessage)); + } + + // Prepare salesforceRequestInterfaceMock responses + if (testScenario.getMocks() != null && testScenario.getMocks().containsKey("SalesforceRequestInterfaceError")) { + String errorMessage = testScenario.getMocks().get("SalesforceRequestInterfaceError").toString(); + + when(salesforceRequestInterfaceMock.execute(any(), any())).thenThrow(new RuntimeException(errorMessage)); + } + } + + private ResultActions performRequest(Scenario testScenario) throws Exception { + // existing code for performing the request + + String url = "/api/v1/accounts/feed"; + String cookieValue = Constants.SALESFORCE_ACTIVE_USER_COOKIE_VALUE; + String paginationIdentifier = testScenario.getInput().get("pagination_identifier").toString(); + + ResultActions resultActions = mockMvc.perform(MockMvcRequestBuilders.get(url) + .cookie(new Cookie(CookieConstants.USER_LOGIN_COOKIE_NAME, cookieValue)) + .param("pagination_identifier", paginationIdentifier) + .contentType(MediaType.APPLICATION_JSON)); + + return resultActions; + } + + private void checkResponse(Scenario testScenario, ResultActions resultActions) throws Exception { + // existing code for checking the response + ObjectMapper objectMapper = new ObjectMapper(); + String expectedOutput = objectMapper.writeValueAsString(testScenario.getOutput()); + String actualOutput = resultActions.andReturn().getResponse().getContentAsString(); + + JsonNode expectedOutputJson = objectMapper.readTree(expectedOutput); + JsonNode actualOutputJson = objectMapper.readTree(actualOutput); + + if (resultActions.andReturn().getResponse().getStatus() == 200) { + assertEquals(expectedOutputJson, actualOutputJson); + } + else { + common.compareErrors(testScenario, actualOutput); + } + } + + static Stream testScenariosProvider() throws IOException { + try { + List testScenarios = loadScenarios(); + return testScenarios.stream(); + } + catch (IOException e) { + throw new RuntimeException("Failed to load test scenarios", e); + } + } + + private static List loadScenarios() throws IOException { + String scenariosPath = "classpath:data/functional/controllers/accountController/getAccountsFeed.scenarios.json"; + Resource resource = new DefaultResourceLoader().getResource(scenariosPath); + ObjectMapper objectMapper = new ObjectMapper(); + return objectMapper.readValue(resource.getInputStream(), new TypeReference>() { + }); + } + +} diff --git a/src/test/java/com/salessparrow/api/functional/controllers/accountController/GetNoteDetailsTest.java b/src/test/java/com/salessparrow/api/functional/controllers/accountController/GetNoteDetailsTest.java index 8aa54afb..84e90a28 100644 --- a/src/test/java/com/salessparrow/api/functional/controllers/accountController/GetNoteDetailsTest.java +++ b/src/test/java/com/salessparrow/api/functional/controllers/accountController/GetNoteDetailsTest.java @@ -1,18 +1,18 @@ package com.salessparrow.api.functional.controllers.accountController; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.MethodSource; - import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; import java.io.IOException; +import java.io.InputStream; import java.util.List; import java.util.stream.Stream; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; @@ -36,6 +36,8 @@ import com.salessparrow.api.helper.Scenario; import com.salessparrow.api.helper.Setup; import com.salessparrow.api.lib.globalConstants.CookieConstants; +import com.salessparrow.api.helper.Constants; + import com.salessparrow.api.lib.httpLib.HttpClient.HttpResponse; import com.salessparrow.api.lib.salesforce.helper.MakeCompositeRequest; import com.salessparrow.api.lib.salesforce.wrappers.SalesforceGetNoteContent; @@ -47,85 +49,98 @@ @WebAppConfiguration @Import({ Setup.class, Cleanup.class, Common.class, LoadFixture.class }) public class GetNoteDetailsTest { - @Autowired - private MockMvc mockMvc; - @Autowired - private Setup setup; + @Autowired + private MockMvc mockMvc; + + @Autowired + private Common common; + + @Autowired + private LoadFixture loadFixture; + + @Autowired + private Setup setup; + + @Autowired + private Cleanup cleanup; + + @MockBean + private SalesforceGetNoteContent salesforceGetNoteContentMock; - @Autowired - private Cleanup cleanup; + @MockBean + private MakeCompositeRequest makeCompositeRequestMock; - @Autowired - private Common common; + @BeforeEach + public void setUp() throws DynamobeeException, IOException { + setup.perform(); + } - @Autowired - private LoadFixture loadFixture; + @AfterEach + public void tearDown() { + cleanup.perform(); + } - @MockBean - private MakeCompositeRequest makeCompositeRequestMock; + @ParameterizedTest + @MethodSource("testScenariosProvider") + public void getNoteDetails(Scenario testScenario) throws Exception { + String currentFunctionName = new Object() { + }.getClass().getEnclosingMethod().getName(); + FixtureData fixtureData = common.loadFixture( + "classpath:fixtures/functional/controllers/accountController/getNoteDetails.fixtures.json", + currentFunctionName); + loadFixture.perform(fixtureData); - @MockBean - private SalesforceGetNoteContent salesforceGetNoteContentMock; + ObjectMapper objectMapper = new ObjectMapper(); - @BeforeEach - public void setUp() throws DynamobeeException, IOException { - setup.perform(); - } + String cookieValue = Constants.SALESFORCE_ACTIVE_USER_COOKIE_VALUE; + String accountId = (String) testScenario.getInput().get("accountId"); + String noteId = (String) testScenario.getInput().get("noteId"); - @AfterEach - public void tearDown() { - cleanup.perform(); - } + HttpResponse getNoteDetailsMockResponse = new HttpResponse(); - @ParameterizedTest - @MethodSource("testScenariosProvider") - public void getNoteDetails(Scenario testScenario) throws Exception{ - String currentFunctionName = new Object(){}.getClass().getEnclosingMethod().getName(); - FixtureData fixtureData = common.loadFixture( - "classpath:fixtures/controllers/accountController/getNoteDetails.fixtures.json", - currentFunctionName - ); - loadFixture.perform(fixtureData); + getNoteDetailsMockResponse + .setResponseBody(objectMapper.writeValueAsString(testScenario.getMocks().get("makeCompositeRequest"))); - ObjectMapper objectMapper = new ObjectMapper(); + when(makeCompositeRequestMock.makePostRequest(any(), any())).thenReturn(getNoteDetailsMockResponse); - String cookieValue = (String) testScenario.getInput().get("cookie"); - String accountId = (String) testScenario.getInput().get("accountId"); - String noteId = (String) testScenario.getInput().get("noteId"); + HttpResponse getNoteContentMockResponse = new HttpResponse(); + getNoteContentMockResponse + .setResponseBody(objectMapper.writeValueAsString(testScenario.getMocks().get("makeWrapperRequest"))); - HttpResponse getNoteDetailsMockResponse = new HttpResponse(); + when(salesforceGetNoteContentMock.getNoteContent(any(), any())).thenReturn(getNoteContentMockResponse); - getNoteDetailsMockResponse.setResponseBody(objectMapper.writeValueAsString(testScenario.getMocks().get("makeCompositeRequest"))); - - when(makeCompositeRequestMock.makePostRequest(any(), any())).thenReturn(getNoteDetailsMockResponse); + String url = "/api/v1/accounts/" + accountId + "/notes/" + noteId; + ResultActions resultActions = mockMvc.perform(MockMvcRequestBuilders.get(url) + .cookie(new Cookie(CookieConstants.USER_LOGIN_COOKIE_NAME, cookieValue)) + .contentType(MediaType.APPLICATION_JSON)); + String expectedOutput = objectMapper.writeValueAsString(testScenario.getOutput()); + String actualOutput = resultActions.andReturn().getResponse().getContentAsString(); - HttpResponse getNoteContentMockResponse = new HttpResponse(); - getNoteContentMockResponse.setResponseBody(objectMapper.writeValueAsString(testScenario.getMocks().get("makeWrapperRequest"))); + if (resultActions.andReturn().getResponse().getStatus() == 200) { + assertEquals(expectedOutput, actualOutput); + } + else { + common.compareErrors(testScenario, actualOutput); + } - when(salesforceGetNoteContentMock.getNoteContent(any(), any())).thenReturn(getNoteContentMockResponse); + } - String url = "/api/v1/accounts/" + accountId + "/notes/" + noteId; - ResultActions resultActions = mockMvc.perform(MockMvcRequestBuilders.get(url) - .cookie(new Cookie(CookieConstants.USER_LOGIN_COOKIE_NAME, cookieValue)) - .contentType(MediaType.APPLICATION_JSON)); + static Stream testScenariosProvider() throws IOException { + List testScenarios = loadScenarios(); + return testScenarios.stream(); + } - String expectedOutput = objectMapper.writeValueAsString(testScenario.getOutput()); - - String actualOutput = resultActions.andReturn().getResponse().getContentAsString(); - assertEquals(expectedOutput, actualOutput); - } + private static List loadScenarios() throws IOException { + String scenariosPath = "classpath:data/functional/controllers/accountController/getNoteDetails.scenarios.json"; + Resource resource = new DefaultResourceLoader().getResource(scenariosPath); + ObjectMapper objectMapper = new ObjectMapper(); - static Stream testScenariosProvider() throws IOException { - List testScenarios = loadScenarios(); - return testScenarios.stream(); - } + try (InputStream inputStream = resource.getInputStream()) { + return objectMapper.readValue(inputStream, new TypeReference>() { + }); + } + } - public static List loadScenarios() throws IOException { - String scenariosPath = "classpath:data/controllers/accountController/getNoteDetails.scenarios.json"; - Resource resource = new DefaultResourceLoader().getResource(scenariosPath); - ObjectMapper objectMapper = new ObjectMapper(); - return objectMapper.readValue(resource.getInputStream(), new TypeReference>() {}); - } } diff --git a/src/test/java/com/salessparrow/api/functional/controllers/accountController/GetNoteListTest.java b/src/test/java/com/salessparrow/api/functional/controllers/accountController/GetNoteListTest.java index cb16b237..6e798a92 100644 --- a/src/test/java/com/salessparrow/api/functional/controllers/accountController/GetNoteListTest.java +++ b/src/test/java/com/salessparrow/api/functional/controllers/accountController/GetNoteListTest.java @@ -9,6 +9,7 @@ import static org.mockito.Mockito.when; import java.io.IOException; +import java.io.InputStream; import java.util.ArrayList; import java.util.List; import java.util.stream.Stream; @@ -38,6 +39,8 @@ import com.salessparrow.api.helper.Scenario; import com.salessparrow.api.helper.Setup; import com.salessparrow.api.lib.globalConstants.CookieConstants; +import com.salessparrow.api.helper.Constants; + import com.salessparrow.api.lib.globalConstants.SalesforceConstants; import com.salessparrow.api.lib.httpLib.HttpClient.HttpResponse; import com.salessparrow.api.lib.salesforce.dto.CompositeRequestDto; @@ -52,104 +55,113 @@ @WebAppConfiguration @Import({ Setup.class, Cleanup.class, Common.class, LoadFixture.class }) public class GetNoteListTest { - @Autowired - private MockMvc mockMvc; - - @Autowired - private Setup setup; - - @Autowired - private Cleanup cleanup; - - @Autowired - private Common common; - - @Autowired - private LoadFixture loadFixture; - - @MockBean - private MakeCompositeRequest makeCompositeRequestMock; - - @MockBean - private SalesforceRequest salesforceOauthRequestMock; - - @BeforeEach - public void setUp() throws DynamobeeException, IOException { - setup.perform(); - } - - @AfterEach - public void tearDown() { - cleanup.perform(); - } - - @ParameterizedTest - @MethodSource("testScenariosProvider") - public void getNoteList(Scenario testScenario) throws Exception{ - String currentFunctionName = new Object(){}.getClass().getEnclosingMethod().getName(); - FixtureData fixtureData = common.loadFixture( - "classpath:fixtures/controllers/accountController/getNotesList.fixtures.json", - currentFunctionName - ); - loadFixture.perform(fixtureData); - - ObjectMapper objectMapper = new ObjectMapper(); - - String cookieValue = (String) testScenario.getInput().get("cookie"); - String accountId = (String) testScenario.getInput().get("accountId"); - List documentIds = (List) testScenario.getInput().get("documentId"); - - if(documentIds == null) { - documentIds = new ArrayList(); - } - - // Mocking the CompositeRequests of the salesforce oauth request - SalesforceQueryBuilder salesforceLib = new SalesforceQueryBuilder(); - String documentIdsQuery = salesforceLib.getContentDocumentIdUrl(accountId); - String documentIdsUrl = new SalesforceConstants().queryUrlPath() + documentIdsQuery; - CompositeRequestDto documentIdsCompositeReq = new CompositeRequestDto("GET", documentIdsUrl, "GetContentDocumentId"); - List compositeRequests = new ArrayList(); - compositeRequests.add(documentIdsCompositeReq); - - - HttpResponse getNotesListIdMockResponse = new HttpResponse(); - getNotesListIdMockResponse.setResponseBody(objectMapper.writeValueAsString(testScenario.getMocks().get("makeCompositeRequest1"))); - - when(makeCompositeRequestMock.makePostRequest(eq(compositeRequests), any())).thenReturn(getNotesListIdMockResponse); - - // Mocking the CompositeRequests of the salesforce notes list request - String notesQuery = salesforceLib.getNoteListIdUrl(documentIds); - String notesUrl = new SalesforceConstants().queryUrlPath() + notesQuery; - CompositeRequestDto noteCompositeRequest = new CompositeRequestDto("GET", notesUrl, "GetNotesList"); - List noteCompositeRequests = new ArrayList(); - noteCompositeRequests.add(noteCompositeRequest); - - - HttpResponse getNotesListMockResponse = new HttpResponse(); - getNotesListMockResponse.setResponseBody(objectMapper.writeValueAsString(testScenario.getMocks().get("makeCompositeRequest2"))); - - when(makeCompositeRequestMock.makePostRequest(eq(noteCompositeRequests), any())).thenReturn(getNotesListMockResponse); - - String url = "/api/v1/accounts/" + accountId + "/notes"; - ResultActions resultActions = mockMvc.perform(MockMvcRequestBuilders.get(url) - .cookie(new Cookie(CookieConstants.USER_LOGIN_COOKIE_NAME, cookieValue)) - .contentType(MediaType.APPLICATION_JSON)); - - String expectedOutput = objectMapper.writeValueAsString(testScenario.getOutput()); - - String actualOutput = resultActions.andReturn().getResponse().getContentAsString(); - assertEquals(expectedOutput, actualOutput); - } - - static Stream testScenariosProvider() throws IOException { - List testScenarios = loadScenarios(); - return testScenarios.stream(); - } - - public static List loadScenarios() throws IOException { - String scenariosPath = "classpath:data/controllers/accountController/getNotesList.scenarios.json"; - Resource resource = new DefaultResourceLoader().getResource(scenariosPath); - ObjectMapper objectMapper = new ObjectMapper(); - return objectMapper.readValue(resource.getInputStream(), new TypeReference>() {}); - } + + @Autowired + private MockMvc mockMvc; + + @Autowired + private Setup setup; + + @Autowired + private Cleanup cleanup; + + @Autowired + private Common common; + + @Autowired + private LoadFixture loadFixture; + + @MockBean + private MakeCompositeRequest makeCompositeRequestMock; + + @MockBean + private SalesforceRequest salesforceOauthRequestMock; + + @BeforeEach + public void setUp() throws DynamobeeException, IOException { + setup.perform(); + } + + @AfterEach + public void tearDown() { + cleanup.perform(); + } + + @ParameterizedTest + @MethodSource("testScenariosProvider") + public void getNoteList(Scenario testScenario) throws Exception { + String currentFunctionName = new Object() { + }.getClass().getEnclosingMethod().getName(); + FixtureData fixtureData = common.loadFixture( + "classpath:fixtures/functional/controllers/accountController/getNotesList.fixtures.json", + currentFunctionName); + loadFixture.perform(fixtureData); + + ObjectMapper objectMapper = new ObjectMapper(); + + String cookieValue = Constants.SALESFORCE_ACTIVE_USER_COOKIE_VALUE; + String accountId = (String) testScenario.getInput().get("accountId"); + List documentIds = (List) testScenario.getInput().get("documentId"); + + if (documentIds == null) { + documentIds = new ArrayList(); + } + + // Mocking the CompositeRequests of the salesforce oauth request + SalesforceQueryBuilder salesforceLib = new SalesforceQueryBuilder(); + String documentIdsQuery = salesforceLib.getContentDocumentIdUrl(accountId); + String documentIdsUrl = new SalesforceConstants().queryUrlPath() + documentIdsQuery; + CompositeRequestDto documentIdsCompositeReq = new CompositeRequestDto("GET", documentIdsUrl, + "GetContentDocumentId"); + List compositeRequests = new ArrayList(); + compositeRequests.add(documentIdsCompositeReq); + + HttpResponse getNotesListIdMockResponse = new HttpResponse(); + getNotesListIdMockResponse + .setResponseBody(objectMapper.writeValueAsString(testScenario.getMocks().get("makeCompositeRequest1"))); + + when(makeCompositeRequestMock.makePostRequest(eq(compositeRequests), any())) + .thenReturn(getNotesListIdMockResponse); + + // Mocking the CompositeRequests of the salesforce notes list request + String notesQuery = salesforceLib.getNoteListIdUrl(documentIds); + String notesUrl = new SalesforceConstants().queryUrlPath() + notesQuery; + CompositeRequestDto noteCompositeRequest = new CompositeRequestDto("GET", notesUrl, "GetNotesList"); + List noteCompositeRequests = new ArrayList(); + noteCompositeRequests.add(noteCompositeRequest); + + HttpResponse getNotesListMockResponse = new HttpResponse(); + getNotesListMockResponse + .setResponseBody(objectMapper.writeValueAsString(testScenario.getMocks().get("makeCompositeRequest2"))); + + when(makeCompositeRequestMock.makePostRequest(eq(noteCompositeRequests), any())) + .thenReturn(getNotesListMockResponse); + + String url = "/api/v1/accounts/" + accountId + "/notes"; + ResultActions resultActions = mockMvc.perform(MockMvcRequestBuilders.get(url) + .cookie(new Cookie(CookieConstants.USER_LOGIN_COOKIE_NAME, cookieValue)) + .contentType(MediaType.APPLICATION_JSON)); + + String expectedOutput = objectMapper.writeValueAsString(testScenario.getOutput()); + + String actualOutput = resultActions.andReturn().getResponse().getContentAsString(); + assertEquals(expectedOutput, actualOutput); + } + + static Stream testScenariosProvider() throws IOException { + List testScenarios = loadScenarios(); + return testScenarios.stream(); + } + + private static List loadScenarios() throws IOException { + String scenariosPath = "classpath:data/functional/controllers/accountController/getNotesList.scenarios.json"; + Resource resource = new DefaultResourceLoader().getResource(scenariosPath); + ObjectMapper objectMapper = new ObjectMapper(); + + try (InputStream inputStream = resource.getInputStream()) { + return objectMapper.readValue(resource.getInputStream(), new TypeReference>() { + }); + } + } + } diff --git a/src/test/java/com/salessparrow/api/functional/controllers/accountNoteController/DeleteAccountNoteTest.java b/src/test/java/com/salessparrow/api/functional/controllers/accountNoteController/DeleteAccountNoteTest.java new file mode 100644 index 00000000..2c5d5cee --- /dev/null +++ b/src/test/java/com/salessparrow/api/functional/controllers/accountNoteController/DeleteAccountNoteTest.java @@ -0,0 +1,128 @@ +package com.salessparrow.api.functional.controllers.accountNoteController; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +import java.io.IOException; +import java.util.List; +import java.util.stream.Stream; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; +import org.springframework.core.io.DefaultResourceLoader; +import org.springframework.core.io.Resource; +import org.springframework.http.MediaType; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.dynamobee.exception.DynamobeeException; +import com.salessparrow.api.helper.Cleanup; +import com.salessparrow.api.helper.Common; +import com.salessparrow.api.helper.Constants; +import com.salessparrow.api.helper.FixtureData; +import com.salessparrow.api.helper.LoadFixture; +import com.salessparrow.api.helper.Scenario; +import com.salessparrow.api.helper.Setup; +import com.salessparrow.api.lib.globalConstants.CookieConstants; +import com.salessparrow.api.lib.httpLib.HttpClient.HttpResponse; +import com.salessparrow.api.lib.salesforce.helper.MakeCompositeRequest; + +import jakarta.servlet.http.Cookie; + +@SpringBootTest +@AutoConfigureMockMvc +@WebAppConfiguration +@Import({ Setup.class, Cleanup.class, Common.class, LoadFixture.class }) +public class DeleteAccountNoteTest { + + @Autowired + private MockMvc mockMvc; + + @Autowired + private Common common; + + @Autowired + private LoadFixture loadFixture; + + @Autowired + private Setup setup; + + @Autowired + private Cleanup cleanup; + + @MockBean + private MakeCompositeRequest makeCompositeRequestMock; + + @BeforeEach + public void setUp() throws DynamobeeException { + setup.perform(); + } + + @AfterEach + public void tearDown() { + cleanup.perform(); + } + + @ParameterizedTest + @MethodSource("testScenariosProvider") + public void deleteAccountNote(Scenario testScenario) throws Exception { + + // Load fixture data + String currentFunctionName = new Object() { + }.getClass().getEnclosingMethod().getName(); + FixtureData fixtureData = common.loadFixture( + "classpath:fixtures/functional/controllers/accountNoteController/deleteAccountNote.fixtures.json", + currentFunctionName); + loadFixture.perform(fixtureData); + + // Read data from the scenario + ObjectMapper objectMapper = new ObjectMapper(); + String cookieValue = Constants.SALESFORCE_ACTIVE_USER_COOKIE_VALUE; + String accountId = (String) testScenario.getInput().get("accountId"); + String noteId = (String) testScenario.getInput().get("noteId"); + + // Prepare mock responses + HttpResponse deleteNoteMockResponse = new HttpResponse(); + deleteNoteMockResponse + .setResponseBody(objectMapper.writeValueAsString(testScenario.getMocks().get("makeCompositeRequest"))); + when(makeCompositeRequestMock.makePostRequest(any(), any())).thenReturn(deleteNoteMockResponse); + + // Perform the request + String url = "/api/v1/accounts/" + accountId + "/notes/" + noteId; + + ResultActions resultActions = mockMvc.perform(MockMvcRequestBuilders.delete(url) + .cookie(new Cookie(CookieConstants.USER_LOGIN_COOKIE_NAME, cookieValue)) + .contentType(MediaType.APPLICATION_JSON)); + + // Check the response + String expectedOutput = objectMapper.writeValueAsString(testScenario.getOutput()); + String actualOutput = resultActions.andReturn().getResponse().getContentAsString(); + common.assertCustomEquals(expectedOutput, actualOutput); + } + + static Stream testScenariosProvider() throws IOException { + List testScenarios = loadScenarios(); + return testScenarios.stream(); + } + + private static List loadScenarios() throws IOException { + String scenariosPath = "classpath:data/functional/controllers/accountNoteController/deleteAccountNote.scenarios.json"; + Resource resource = new DefaultResourceLoader().getResource(scenariosPath); + ObjectMapper objectMapper = new ObjectMapper(); + return objectMapper.readValue(resource.getInputStream(), new TypeReference>() { + }); + } + +} diff --git a/src/test/java/com/salessparrow/api/functional/controllers/accountTaskController/CreateTaskTest.java b/src/test/java/com/salessparrow/api/functional/controllers/accountTaskController/CreateTaskTest.java new file mode 100644 index 00000000..307edeb1 --- /dev/null +++ b/src/test/java/com/salessparrow/api/functional/controllers/accountTaskController/CreateTaskTest.java @@ -0,0 +1,135 @@ +package com.salessparrow.api.functional.controllers.accountTaskController; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +import java.io.IOException; +import java.util.List; +import java.util.stream.Stream; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; +import org.springframework.core.io.DefaultResourceLoader; +import org.springframework.core.io.Resource; +import org.springframework.http.MediaType; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.dynamobee.exception.DynamobeeException; +import com.salessparrow.api.helper.Cleanup; +import com.salessparrow.api.helper.Common; +import com.salessparrow.api.helper.Constants; +import com.salessparrow.api.helper.FixtureData; +import com.salessparrow.api.helper.LoadFixture; +import com.salessparrow.api.helper.Scenario; +import com.salessparrow.api.helper.Setup; +import com.salessparrow.api.lib.globalConstants.CookieConstants; +import com.salessparrow.api.lib.httpLib.HttpClient.HttpResponse; +import com.salessparrow.api.lib.salesforce.helper.MakeCompositeRequest; + +import jakarta.servlet.http.Cookie; + +@SpringBootTest +@AutoConfigureMockMvc +@WebAppConfiguration +@Import({ Setup.class, Cleanup.class, Common.class, LoadFixture.class }) +public class CreateTaskTest { + + @Autowired + private MockMvc mockMvc; + + @Autowired + private Common common; + + @Autowired + private LoadFixture loadFixture; + + @Autowired + private Setup setup; + + @Autowired + private Cleanup cleanup; + + @MockBean + private MakeCompositeRequest makeCompositeRequestMock; + + @BeforeEach + public void setUp() throws DynamobeeException { + setup.perform(); + } + + @AfterEach + public void tearDown() { + cleanup.perform(); + } + + @ParameterizedTest + @MethodSource("testScenariosProvider") + public void createTask(Scenario testScenario) throws Exception { + + // Load fixture data + String currentFunctionName = new Object() { + }.getClass().getEnclosingMethod().getName(); + FixtureData fixtureData = common.loadFixture( + "classpath:fixtures/functional/controllers/accountTaskController/createTask.fixtures.json", + currentFunctionName); + loadFixture.perform(fixtureData); + + // Read data from the scenario + ObjectMapper objectMapper = new ObjectMapper(); + String cookieValue = Constants.SALESFORCE_ACTIVE_USER_COOKIE_VALUE; + String accountId = (String) testScenario.getInput().get("accountId"); + + // Prepare mock responses + HttpResponse createNoteMockResponse = new HttpResponse(); + createNoteMockResponse + .setResponseBody(objectMapper.writeValueAsString(testScenario.getMocks().get("makeCompositeRequest"))); + when(makeCompositeRequestMock.makePostRequest(any(), any())).thenReturn(createNoteMockResponse); + + // Perform the request + String requestBody = objectMapper.writeValueAsString(testScenario.getInput().get("body")); + String url = "/api/v1/accounts/" + accountId + "/tasks"; + + ResultActions resultActions = mockMvc.perform(MockMvcRequestBuilders.post(url) + .cookie(new Cookie(CookieConstants.USER_LOGIN_COOKIE_NAME, cookieValue)) + .content(requestBody) + .contentType(MediaType.APPLICATION_JSON)); + + // Check the response + String expectedOutput = objectMapper.writeValueAsString(testScenario.getOutput()); + String actualOutput = resultActions.andReturn().getResponse().getContentAsString(); + + if (resultActions.andReturn().getResponse().getStatus() == 201) { + assertEquals(expectedOutput, actualOutput); + } + else { + common.compareErrors(testScenario, actualOutput); + } + } + + static Stream testScenariosProvider() throws IOException { + List testScenarios = loadScenarios(); + return testScenarios.stream(); + } + + private static List loadScenarios() throws IOException { + String scenariosPath = "classpath:data/functional/controllers/accountTaskController/createTask.scenarios.json"; + Resource resource = new DefaultResourceLoader().getResource(scenariosPath); + ObjectMapper objectMapper = new ObjectMapper(); + return objectMapper.readValue(resource.getInputStream(), new TypeReference>() { + }); + } + +} diff --git a/src/test/java/com/salessparrow/api/functional/controllers/accountTaskController/DeleteAccountTaskTest.java b/src/test/java/com/salessparrow/api/functional/controllers/accountTaskController/DeleteAccountTaskTest.java new file mode 100644 index 00000000..538beecd --- /dev/null +++ b/src/test/java/com/salessparrow/api/functional/controllers/accountTaskController/DeleteAccountTaskTest.java @@ -0,0 +1,139 @@ +package com.salessparrow.api.functional.controllers.accountTaskController; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +import java.io.IOException; +import java.util.List; +import java.util.stream.Stream; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; +import org.springframework.core.io.DefaultResourceLoader; +import org.springframework.core.io.Resource; +import org.springframework.http.MediaType; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.dynamobee.exception.DynamobeeException; +import com.salessparrow.api.helper.Cleanup; +import com.salessparrow.api.helper.Common; +import com.salessparrow.api.helper.Constants; +import com.salessparrow.api.helper.FixtureData; +import com.salessparrow.api.helper.LoadFixture; +import com.salessparrow.api.helper.Scenario; +import com.salessparrow.api.helper.Setup; +import com.salessparrow.api.lib.globalConstants.CookieConstants; +import com.salessparrow.api.lib.httpLib.HttpClient.HttpResponse; +import com.salessparrow.api.lib.salesforce.helper.MakeCompositeRequest; + +import jakarta.servlet.http.Cookie; + +@SpringBootTest +@AutoConfigureMockMvc +@WebAppConfiguration +@Import({ Setup.class, Cleanup.class, Common.class, LoadFixture.class }) +public class DeleteAccountTaskTest { + + @Autowired + private MockMvc mockMvc; + + @Autowired + private Common common; + + @Autowired + private LoadFixture loadFixture; + + @Autowired + private Setup setup; + + @Autowired + private Cleanup cleanup; + + @MockBean + private MakeCompositeRequest makeCompositeRequestMock; + + @BeforeEach + public void setUp() throws DynamobeeException { + setup.perform(); + } + + @AfterEach + public void tearDown() { + cleanup.perform(); + } + + @ParameterizedTest + @MethodSource("testScenariosProvider") + public void deleteAccountTask(Scenario testScenario) throws Exception { + + // Load fixture data + String currentFunctionName = new Object() { + }.getClass().getEnclosingMethod().getName(); + FixtureData fixtureData = common.loadFixture( + "classpath:fixtures/functional/controllers/accountTaskController/deleteAccountTask.fixtures.json", + currentFunctionName); + loadFixture.perform(fixtureData); + + // Read data from the scenario + ObjectMapper objectMapper = new ObjectMapper(); + String cookieValue = Constants.SALESFORCE_ACTIVE_USER_COOKIE_VALUE; + String accountId = (String) testScenario.getInput().get("accountId"); + String taskId = (String) testScenario.getInput().get("taskId"); + + // Prepare mock responses + HttpResponse createNoteMockResponse = new HttpResponse(); + createNoteMockResponse + .setResponseBody(objectMapper.writeValueAsString(testScenario.getMocks().get("makeCompositeRequest"))); + when(makeCompositeRequestMock.makePostRequest(any(), any())).thenReturn(createNoteMockResponse); + + // Perform the request + String requestBody = objectMapper.writeValueAsString(testScenario.getInput().get("body")); + String url = "/api/v1/accounts/" + accountId + "/tasks/" + taskId; + + ResultActions resultActions = mockMvc.perform(MockMvcRequestBuilders.delete(url) + .cookie(new Cookie(CookieConstants.USER_LOGIN_COOKIE_NAME, cookieValue)) + .content(requestBody) + .contentType(MediaType.APPLICATION_JSON)); + + // Check the response + String expectedOutput = objectMapper.writeValueAsString(testScenario.getOutput()); + String actualOutput = resultActions.andReturn().getResponse().getContentAsString(); + + if (resultActions.andReturn().getResponse().getStatus() == 204) { + if (expectedOutput.equals("{}")) { + expectedOutput = ""; + } + assertEquals(expectedOutput, actualOutput); + } + else { + common.compareErrors(testScenario, actualOutput); + } + } + + static Stream testScenariosProvider() throws IOException { + List testScenarios = loadScenarios(); + return testScenarios.stream(); + } + + private static List loadScenarios() throws IOException { + String scenariosPath = "classpath:data/functional/controllers/accountTaskController/deleteAccountTask.scenarios.json"; + Resource resource = new DefaultResourceLoader().getResource(scenariosPath); + ObjectMapper objectMapper = new ObjectMapper(); + return objectMapper.readValue(resource.getInputStream(), new TypeReference>() { + }); + } + +} diff --git a/src/test/java/com/salessparrow/api/functional/controllers/accountTaskController/GetAccountTasksListTest.java b/src/test/java/com/salessparrow/api/functional/controllers/accountTaskController/GetAccountTasksListTest.java new file mode 100644 index 00000000..3914c29f --- /dev/null +++ b/src/test/java/com/salessparrow/api/functional/controllers/accountTaskController/GetAccountTasksListTest.java @@ -0,0 +1,127 @@ +package com.salessparrow.api.functional.controllers.accountTaskController; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +import java.io.IOException; +import java.util.List; +import java.util.stream.Stream; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; +import org.springframework.core.io.DefaultResourceLoader; +import org.springframework.core.io.Resource; +import org.springframework.http.MediaType; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.dynamobee.exception.DynamobeeException; +import com.salessparrow.api.helper.Cleanup; +import com.salessparrow.api.helper.Common; +import com.salessparrow.api.helper.Constants; +import com.salessparrow.api.helper.FixtureData; +import com.salessparrow.api.helper.LoadFixture; +import com.salessparrow.api.helper.Scenario; +import com.salessparrow.api.helper.Setup; +import com.salessparrow.api.lib.globalConstants.CookieConstants; +import com.salessparrow.api.lib.httpLib.HttpClient.HttpResponse; +import com.salessparrow.api.lib.salesforce.helper.MakeCompositeRequest; + +import jakarta.servlet.http.Cookie; + +@SpringBootTest +@AutoConfigureMockMvc +@WebAppConfiguration +@Import({ Setup.class, Cleanup.class, Common.class, LoadFixture.class }) +public class GetAccountTasksListTest { + + @Autowired + private MockMvc mockMvc; + + @Autowired + private Common common; + + @Autowired + private LoadFixture loadFixture; + + @Autowired + private Setup setup; + + @Autowired + private Cleanup cleanup; + + @MockBean + private MakeCompositeRequest makeCompositeRequestMock; + + @BeforeEach + public void setUp() throws DynamobeeException { + setup.perform(); + } + + @AfterEach + public void tearDown() { + cleanup.perform(); + } + + @ParameterizedTest + @MethodSource("testScenariosProvider") + public void getAccountTasksList(Scenario testScenario) throws Exception { + + // Load fixture data + String currentFunctionName = new Object() { + }.getClass().getEnclosingMethod().getName(); + FixtureData fixtureData = common.loadFixture( + "classpath:fixtures/functional/controllers/accountTaskController/getAccountTasksList.fixtures.json", + currentFunctionName); + loadFixture.perform(fixtureData); + + // Read data from the scenario + ObjectMapper objectMapper = new ObjectMapper(); + String cookieValue = Constants.SALESFORCE_ACTIVE_USER_COOKIE_VALUE; + String accountId = (String) testScenario.getInput().get("accountId"); + + // Prepare mock responses + HttpResponse getTasksListMockResponse = new HttpResponse(); + getTasksListMockResponse + .setResponseBody(objectMapper.writeValueAsString(testScenario.getMocks().get("makeCompositeRequest"))); + when(makeCompositeRequestMock.makePostRequest(any(), any())).thenReturn(getTasksListMockResponse); + + // Perform the request + String url = "/api/v1/accounts/" + accountId + "/tasks"; + + ResultActions resultActions = mockMvc.perform(MockMvcRequestBuilders.get(url) + .cookie(new Cookie(CookieConstants.USER_LOGIN_COOKIE_NAME, cookieValue)) + .contentType(MediaType.APPLICATION_JSON)); + + // Check the response + String expectedOutput = objectMapper.writeValueAsString(testScenario.getOutput()); + String actualOutput = resultActions.andReturn().getResponse().getContentAsString(); + assertEquals(expectedOutput, actualOutput); + } + + static Stream testScenariosProvider() throws IOException { + List testScenarios = loadScenarios(); + return testScenarios.stream(); + } + + private static List loadScenarios() throws IOException { + String scenariosPath = "classpath:data/functional/controllers/accountTaskController/getAccountTasksList.scenarios.json"; + Resource resource = new DefaultResourceLoader().getResource(scenariosPath); + ObjectMapper objectMapper = new ObjectMapper(); + return objectMapper.readValue(resource.getInputStream(), new TypeReference>() { + }); + } + +} diff --git a/src/test/java/com/salessparrow/api/functional/controllers/authController/GetRedirectUrlTest.java b/src/test/java/com/salessparrow/api/functional/controllers/authController/GetRedirectUrlTest.java index 4d8b7871..535cb06f 100644 --- a/src/test/java/com/salessparrow/api/functional/controllers/authController/GetRedirectUrlTest.java +++ b/src/test/java/com/salessparrow/api/functional/controllers/authController/GetRedirectUrlTest.java @@ -24,31 +24,36 @@ @WebAppConfiguration @Import({ Setup.class, Cleanup.class, Common.class }) public class GetRedirectUrlTest { - @Autowired - private MockMvc mockMvc; - - @Autowired - private Common common; - - @Test - public void getRedirectUrl() throws Exception{ - List testDataItems = common.loadScenariosData("classpath:data/controllers/authController/redirectUrl.scenarios.json"); - - for (Scenario testDataItem : testDataItems) { - ObjectMapper objectMapper = new ObjectMapper(); - String expectedOutput = objectMapper.writeValueAsString(testDataItem.getOutput()); - - ResultActions resultActions = mockMvc.perform(MockMvcRequestBuilders.get("/api/v1/auth/salesforce/redirect-url") - .param("redirect_uri", (String) testDataItem.getInput().get("redirect_uri")) - .param("state", (String) testDataItem.getInput().get("state")) - .contentType(MediaType.APPLICATION_JSON)); - - String contentAsString = resultActions.andReturn().getResponse().getContentAsString(); - if(resultActions.andReturn().getResponse().getStatus() == 200) { - assertEquals(expectedOutput, contentAsString); - } else { - common.compareErrors(testDataItem, contentAsString); - } - } - } + + @Autowired + private MockMvc mockMvc; + + @Autowired + private Common common; + + @Test + public void getRedirectUrl() throws Exception { + List testDataItems = common + .loadScenariosData("classpath:data/functional/controllers/authController/redirectUrl.scenarios.json"); + + for (Scenario testDataItem : testDataItems) { + ObjectMapper objectMapper = new ObjectMapper(); + String expectedOutput = objectMapper.writeValueAsString(testDataItem.getOutput()); + + ResultActions resultActions = mockMvc + .perform(MockMvcRequestBuilders.get("/api/v1/auth/salesforce/redirect-url") + .param("redirect_uri", (String) testDataItem.getInput().get("redirect_uri")) + .param("state", (String) testDataItem.getInput().get("state")) + .contentType(MediaType.APPLICATION_JSON)); + + String contentAsString = resultActions.andReturn().getResponse().getContentAsString(); + if (resultActions.andReturn().getResponse().getStatus() == 200) { + assertEquals(expectedOutput, contentAsString); + } + else { + common.compareErrors(testDataItem, contentAsString); + } + } + } + } diff --git a/src/test/java/com/salessparrow/api/functional/controllers/authController/PostDisconnectTest.java b/src/test/java/com/salessparrow/api/functional/controllers/authController/PostDisconnectTest.java new file mode 100644 index 00000000..6f0499ee --- /dev/null +++ b/src/test/java/com/salessparrow/api/functional/controllers/authController/PostDisconnectTest.java @@ -0,0 +1,146 @@ +package com.salessparrow.api.functional.controllers.authController; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.when; + +import java.io.IOException; +import java.util.List; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.MockitoAnnotations; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; +import org.springframework.http.MediaType; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.dynamobee.exception.DynamobeeException; +import com.salessparrow.api.helper.Cleanup; +import com.salessparrow.api.helper.Common; +import com.salessparrow.api.helper.Constants; +import com.salessparrow.api.helper.FixtureData; +import com.salessparrow.api.helper.LoadFixture; +import com.salessparrow.api.helper.Scenario; +import com.salessparrow.api.helper.Setup; +import com.salessparrow.api.lib.globalConstants.CookieConstants; +import com.salessparrow.api.lib.httpLib.HttpClient.HttpResponse; +import com.salessparrow.api.lib.salesforce.wrappers.SalesforceTokens; + +import jakarta.servlet.http.Cookie; + +@SpringBootTest +@AutoConfigureMockMvc +@WebAppConfiguration +@Import({ Setup.class, Cleanup.class, Common.class, LoadFixture.class }) +public class PostDisconnectTest { + + @Autowired + private MockMvc mockMvc; + + @Autowired + private Common common; + + @Autowired + private Setup setup; + + @Autowired + private Cleanup cleanup; + + @Autowired + private LoadFixture loadFixture; + + @MockBean + private SalesforceTokens mockGetTokens; + + @BeforeEach + public void setUp() throws DynamobeeException, IOException { + MockitoAnnotations.openMocks(this); + setup.perform(); + } + + @AfterEach + public void tearDown() { + cleanup.perform(); + } + + @Test + public void testPostDisconnectSuccess() throws Exception { + String currentFunctionName = new Object() { + }.getClass().getEnclosingMethod().getName(); + FixtureData fixtureData = common.loadFixture( + "classpath:fixtures/functional/controllers/authController/PostDisconnectFixture.json", + currentFunctionName); + loadFixture.perform(fixtureData); + + List testDataItems = common.loadScenariosData( + "classpath:data/functional/controllers/authController/Disconnect.scenarios.json", currentFunctionName); + + for (Scenario testDataItem : testDataItems) { + ObjectMapper objectMapper = new ObjectMapper(); + String cookieValue = Constants.SALESFORCE_ACTIVE_USER_COOKIE_VALUE; + + HttpResponse getTokensMockRes = new HttpResponse(); + getTokensMockRes + .setResponseBody(objectMapper.writeValueAsString(testDataItem.getMocks().get("revokeTokens"))); + + when(mockGetTokens.getTokens(anyString(), anyString())).thenReturn(getTokensMockRes); + + ResultActions resultActions = mockMvc.perform(MockMvcRequestBuilders.post("/api/v1/auth/disconnect") + .cookie(new Cookie(CookieConstants.USER_LOGIN_COOKIE_NAME, cookieValue)) + .contentType(MediaType.APPLICATION_JSON)); + + String actualOutput = resultActions.andReturn().getResponse().getContentAsString(); + + if (testDataItem.getOutput().get("success").equals(true)) { + assertEquals(204, resultActions.andReturn().getResponse().getStatus()); + } + else { + common.compareErrors(testDataItem, actualOutput); + } + } + } + + @Test + public void testPostDisconnectNoTokens() throws Exception { + String currentFunctionName = new Object() { + }.getClass().getEnclosingMethod().getName(); + FixtureData fixtureData = common.loadFixture( + "classpath:fixtures/functional/controllers/authController/PostDisconnectFixture.json", + currentFunctionName); + loadFixture.perform(fixtureData); + + List testDataItems = common.loadScenariosData( + "classpath:data/functional/controllers/authController/Disconnect.scenarios.json", currentFunctionName); + + for (Scenario testDataItem : testDataItems) { + ObjectMapper objectMapper = new ObjectMapper(); + String cookieValue = Constants.SALESFORCE_ACTIVE_USER_COOKIE_VALUE; + + HttpResponse getTokensMockRes = new HttpResponse(); + getTokensMockRes + .setResponseBody(objectMapper.writeValueAsString(testDataItem.getMocks().get("revokeTokens"))); + + when(mockGetTokens.getTokens(anyString(), anyString())).thenReturn(getTokensMockRes); + + ResultActions resultActions = mockMvc.perform(MockMvcRequestBuilders.post("/api/v1/auth/disconnect") + .cookie(new Cookie(CookieConstants.USER_LOGIN_COOKIE_NAME, cookieValue)) + .contentType(MediaType.APPLICATION_JSON)); + + String actualOutput = resultActions.andReturn().getResponse().getContentAsString(); + + if (resultActions.andReturn().getResponse().getStatus() != 204) { + common.compareErrors(testDataItem, actualOutput); + } + } + } + +} diff --git a/src/test/java/com/salessparrow/api/functional/controllers/authController/PostLogoutTest.java b/src/test/java/com/salessparrow/api/functional/controllers/authController/PostLogoutTest.java index a889b869..e70a5f5d 100644 --- a/src/test/java/com/salessparrow/api/functional/controllers/authController/PostLogoutTest.java +++ b/src/test/java/com/salessparrow/api/functional/controllers/authController/PostLogoutTest.java @@ -1,35 +1,32 @@ package com.salessparrow.api.functional.controllers.authController; import java.io.IOException; -import java.util.HashMap; import java.util.List; -import java.util.Map; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.annotation.Import; -import org.springframework.core.io.Resource; -import org.springframework.core.io.ResourceLoader; import org.springframework.http.MediaType; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.ResultActions; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; -import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import com.github.dynamobee.exception.DynamobeeException; -import com.google.common.net.HttpHeaders; import com.salessparrow.api.helper.Cleanup; import com.salessparrow.api.helper.Common; import com.salessparrow.api.helper.FixtureData; import com.salessparrow.api.helper.LoadFixture; import com.salessparrow.api.helper.Scenario; import com.salessparrow.api.helper.Setup; + import com.salessparrow.api.lib.globalConstants.CookieConstants; import jakarta.servlet.http.Cookie; @@ -39,76 +36,63 @@ @WebAppConfiguration @Import({ Setup.class, Cleanup.class, Common.class, LoadFixture.class }) public class PostLogoutTest { - @Autowired - private ResourceLoader resourceLoader; - - @Autowired - private MockMvc mockMvc; - - @Autowired - private Setup setup; - - @Autowired - private Cleanup cleanup; - - @Autowired - private Common common; - - @Autowired - private LoadFixture loadFixture; - - @BeforeEach - public void setUp() throws DynamobeeException, IOException { - setup.perform(); - } - - @AfterEach - public void tearDown() { - cleanup.perform(); - } - - @Test - public void testPostLogout() throws Exception { - String currentFunctionName = new Object() { - }.getClass().getEnclosingMethod().getName(); - - FixtureData fixtureData = common.loadFixture( - "classpath:fixtures/controllers/authController/PostLogoutFixture.json", - currentFunctionName); - loadFixture.perform(fixtureData); - - List testDataItems = loadTestData(currentFunctionName); - - for (Scenario testDataItem : testDataItems) { - System.out.println("Test description: " + testDataItem.getDescription()); - ObjectMapper objectMapper = new ObjectMapper(); - String expectedOutput = objectMapper.writeValueAsString(testDataItem.getOutput()); - String cookieValue = (String) testDataItem.getInput().get("cookie"); - - ResultActions resultActions = mockMvc.perform(MockMvcRequestBuilders.post("/api/v1/auth/logout") - .cookie(new Cookie(CookieConstants.USER_LOGIN_COOKIE_NAME, cookieValue)) - .contentType(MediaType.APPLICATION_JSON)); - - String actualOutput = resultActions.andReturn().getResponse().getContentAsString(); - - if (resultActions.andReturn().getResponse().getStatus() != 200) { - System.out.println("Expected output: " + expectedOutput); - System.out.println("Actual output: " + actualOutput); - System.out.println("Status code: " + resultActions.andReturn().getResponse().getStatus()); - common.compareErrors(testDataItem, actualOutput); - } - } - } - - public List loadTestData(String key) throws IOException { - String scenariosPath = "classpath:data/controllers/authController/Logout.scenarios.json"; - Resource resource = resourceLoader.getResource(scenariosPath); - ObjectMapper objectMapper = new ObjectMapper(); - - Map> scenariosMap = new HashMap<>(); - scenariosMap = objectMapper.readValue(resource.getInputStream(), - new TypeReference>>() { - }); - return scenariosMap.get(key); - } -} + + @Autowired + private MockMvc mockMvc; + + @Autowired + private Setup setup; + + @Autowired + private Cleanup cleanup; + + @Autowired + private Common common; + + @Autowired + private LoadFixture loadFixture; + + Logger logger = LoggerFactory.getLogger(PostLogoutTest.class); + + @BeforeEach + public void setUp() throws DynamobeeException, IOException { + setup.perform(); + } + + @AfterEach + public void tearDown() { + cleanup.perform(); + } + + @Test + public void testPostLogout() throws Exception { + String currentFunctionName = new Object() { + }.getClass().getEnclosingMethod().getName(); + + FixtureData fixtureData = common.loadFixture( + "classpath:fixtures/functional/controllers/authController/PostLogoutFixture.json", currentFunctionName); + loadFixture.perform(fixtureData); + + List testDataItems = common.loadScenariosData( + "classpath:data/functional/controllers/authController/Logout.scenarios.json", currentFunctionName); + + for (Scenario testDataItem : testDataItems) { + logger.info("Running test scenario: " + testDataItem.getDescription()); + ObjectMapper objectMapper = new ObjectMapper(); + String expectedOutput = objectMapper.writeValueAsString(testDataItem.getOutput()); + logger.info("Expected output: " + expectedOutput); + String cookieValue = (String) testDataItem.getInput().get("cookie"); + + ResultActions resultActions = mockMvc.perform(MockMvcRequestBuilders.post("/api/v1/auth/logout") + .cookie(new Cookie(CookieConstants.USER_LOGIN_COOKIE_NAME, cookieValue)) + .contentType(MediaType.APPLICATION_JSON)); + + String actualOutput = resultActions.andReturn().getResponse().getContentAsString(); + + if (resultActions.andReturn().getResponse().getStatus() != 200) { + common.compareErrors(testDataItem, actualOutput); + } + } + } + +} \ No newline at end of file diff --git a/src/test/java/com/salessparrow/api/functional/controllers/authController/PostSalesforceConnectTest.java b/src/test/java/com/salessparrow/api/functional/controllers/authController/PostSalesforceConnectTest.java index 0408bce9..df83fe06 100644 --- a/src/test/java/com/salessparrow/api/functional/controllers/authController/PostSalesforceConnectTest.java +++ b/src/test/java/com/salessparrow/api/functional/controllers/authController/PostSalesforceConnectTest.java @@ -4,6 +4,8 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.anyString; @@ -12,9 +14,7 @@ import static org.mockito.Mockito.when; import java.io.IOException; -import java.util.HashMap; import java.util.List; -import java.util.Map; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -23,15 +23,12 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Import; -import org.springframework.core.io.Resource; -import org.springframework.core.io.ResourceLoader; import org.springframework.http.MediaType; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.ResultActions; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; -import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import com.github.dynamobee.exception.DynamobeeException; import com.salessparrow.api.helper.Cleanup; @@ -41,7 +38,7 @@ import com.salessparrow.api.helper.Scenario; import com.salessparrow.api.helper.Setup; import com.salessparrow.api.lib.salesforce.wrappers.SalesforceGetIdentity; -import com.salessparrow.api.lib.salesforce.wrappers.SalesforceGetTokens; +import com.salessparrow.api.lib.salesforce.wrappers.SalesforceTokens; import com.salessparrow.api.services.salesforce.AuthService; import com.salessparrow.api.lib.httpLib.HttpClient.HttpResponse; @@ -50,135 +47,183 @@ @WebAppConfiguration @Import({ Setup.class, Cleanup.class, Common.class, LoadFixture.class }) public class PostSalesforceConnectTest { - @Autowired - private ResourceLoader resourceLoader; - @Autowired - private MockMvc mockMvc; + @Autowired + private MockMvc mockMvc; - @Autowired - private Setup setup; + @Autowired + private Setup setup; - @Autowired - private Cleanup cleanup; + @Autowired + private Cleanup cleanup; - @Autowired - private Common common; + @Autowired + private Common common; - @Autowired - private LoadFixture loadFixture; + @Autowired + private LoadFixture loadFixture; - @MockBean - private SalesforceGetTokens mockGetTokens; + @MockBean + private SalesforceTokens mockGetTokens; - @MockBean - private SalesforceGetIdentity mockGetIdentity; + @MockBean + private SalesforceGetIdentity mockGetIdentity; - @Mock - private HttpResponse getTokensHttpMockResponse; - - @InjectMocks - private AuthService authService; - - @BeforeEach - public void setUp() throws DynamobeeException, IOException { - MockitoAnnotations.openMocks(this); - setup.perform(); - } - - @AfterEach - public void tearDown() { - cleanup.perform(); - } - - @Test - public void testPostSalesforceConnectSignup() throws Exception { - String currentFunctionName = new Object() { - }.getClass().getEnclosingMethod().getName(); - - List testDataItems = loadTestData(currentFunctionName); - - for (Scenario testDataItem : testDataItems) { - System.out.println("Test description: " + testDataItem.getDescription()); - ObjectMapper objectMapper = new ObjectMapper(); - - HttpResponse getTokensMockRes = new HttpResponse(); - getTokensMockRes.setResponseBody(objectMapper.writeValueAsString(testDataItem.getMocks().get("getTokens"))); - - HttpResponse getIdentityMockRes = new HttpResponse(); - getIdentityMockRes.setResponseBody(objectMapper.writeValueAsString(testDataItem.getMocks().get("getIdentity"))); - - when(mockGetTokens.getTokens(anyString(), anyString())).thenReturn(getTokensMockRes); - when(mockGetIdentity.getUserIdentity(anyString(), anyString())).thenReturn(getIdentityMockRes); - - ResultActions resultActions = mockMvc.perform(MockMvcRequestBuilders.post("/api/v1/auth/salesforce/connect") - .content(objectMapper.writeValueAsString(testDataItem.getInput().get("body"))) - .accept(MediaType.APPLICATION_JSON).characterEncoding("UTF-8") - .contentType(MediaType.APPLICATION_JSON)); - - String actualOutput = resultActions.andReturn().getResponse().getContentAsString(); - - if (resultActions.andReturn().getResponse().getStatus() == 200) { - assertEquals(objectMapper.writeValueAsString(testDataItem.getOutput()), actualOutput); - } else if (resultActions.andReturn().getResponse().getStatus() == 400) { - common.compareErrors(testDataItem, actualOutput); - } else { - assertEquals(testDataItem.getOutput().get("http_code"), resultActions.andReturn().getResponse().getStatus()); - } - } - } - - @Test - public void testPostSalesforceConnectLogin() throws Exception { - String currentFunctionName = new Object() { - }.getClass().getEnclosingMethod().getName(); - - FixtureData fixtureData = common.loadFixture( - "classpath:fixtures/controllers/authController/PostSalesforceConnectFixture.json", - currentFunctionName); - loadFixture.perform(fixtureData); - - List testDataItems = loadTestData(currentFunctionName); - - for (Scenario testDataItem : testDataItems) { - System.out.println("Test description: " + testDataItem.getDescription()); - ObjectMapper objectMapper = new ObjectMapper(); - - HttpResponse getTokensMockRes = new HttpResponse(); - getTokensMockRes.setResponseBody(objectMapper.writeValueAsString(testDataItem.getMocks().get("getTokens"))); - - when(mockGetTokens.getTokens(anyString(), anyString())).thenReturn(getTokensMockRes); - - ResultActions resultActions = mockMvc.perform(MockMvcRequestBuilders.post("/api/v1/auth/salesforce/connect") - .content(objectMapper.writeValueAsString(testDataItem.getInput().get("body"))) - .accept(MediaType.APPLICATION_JSON).characterEncoding("UTF-8") - .contentType(MediaType.APPLICATION_JSON)); - - String actualOutput = resultActions.andReturn().getResponse().getContentAsString(); - - if (resultActions.andReturn().getResponse().getStatus() == 200) { - assertEquals(objectMapper.writeValueAsString(testDataItem.getOutput()), actualOutput); - verify(mockGetTokens, times(1)).getTokens(anyString(), anyString()); - verify(mockGetIdentity, times(0)).getUserIdentity(anyString(), anyString()); - } else if (resultActions.andReturn().getResponse().getStatus() == 400) { - common.compareErrors(testDataItem, actualOutput); - } else { - assertEquals(testDataItem.getOutput().get("http_code"), resultActions.andReturn().getResponse().getStatus()); - } - } - - } - - public List loadTestData(String key) throws IOException { - String scenariosPath = "classpath:data/controllers/authController/SalesforceConnect.scenarios.json"; - Resource resource = resourceLoader.getResource(scenariosPath); - ObjectMapper objectMapper = new ObjectMapper(); - - Map> scenariosMap = new HashMap<>(); - scenariosMap = objectMapper.readValue(resource.getInputStream(), - new TypeReference>>() { - }); - return scenariosMap.get(key); - } - -} + @Mock + private HttpResponse getTokensHttpMockResponse; + + @InjectMocks + private AuthService authService; + + Logger logger = LoggerFactory.getLogger(PostSalesforceConnectTest.class); + + @BeforeEach + public void setUp() throws DynamobeeException, IOException { + MockitoAnnotations.openMocks(this); + setup.perform(); + } + + @AfterEach + public void tearDown() { + cleanup.perform(); + } + + @Test + public void testPostSalesforceConnectSignup() throws Exception { + String currentFunctionName = new Object() { + }.getClass().getEnclosingMethod().getName(); + + List testDataItems = common.loadScenariosData( + "classpath:data/functional/controllers/authController/SalesforceConnect.scenarios.json", + currentFunctionName); + + for (Scenario testDataItem : testDataItems) { + logger.info("Running test scenario: " + testDataItem.getDescription()); + ObjectMapper objectMapper = new ObjectMapper(); + + HttpResponse getTokensMockRes = new HttpResponse(); + getTokensMockRes.setResponseBody(objectMapper.writeValueAsString(testDataItem.getMocks().get("getTokens"))); + + HttpResponse getIdentityMockRes = new HttpResponse(); + getIdentityMockRes + .setResponseBody(objectMapper.writeValueAsString(testDataItem.getMocks().get("getIdentity"))); + + when(mockGetTokens.getTokens(anyString(), anyString())).thenReturn(getTokensMockRes); + when(mockGetIdentity.getUserIdentity(anyString(), anyString())).thenReturn(getIdentityMockRes); + + ResultActions resultActions = mockMvc.perform(MockMvcRequestBuilders.post("/api/v1/auth/salesforce/connect") + .content(objectMapper.writeValueAsString(testDataItem.getInput().get("body"))) + .accept(MediaType.APPLICATION_JSON) + .characterEncoding("UTF-8") + .contentType(MediaType.APPLICATION_JSON)); + + String actualOutput = resultActions.andReturn().getResponse().getContentAsString(); + + if (resultActions.andReturn().getResponse().getStatus() == 200) { + assertEquals(objectMapper.writeValueAsString(testDataItem.getOutput()), actualOutput); + } + else if (resultActions.andReturn().getResponse().getStatus() == 400) { + common.compareErrors(testDataItem, actualOutput); + } + else { + assertEquals(testDataItem.getOutput().get("http_code"), + resultActions.andReturn().getResponse().getStatus()); + } + } + } + + @Test + public void testPostSalesforceConnectLogin() throws Exception { + String currentFunctionName = new Object() { + }.getClass().getEnclosingMethod().getName(); + + FixtureData fixtureData = common.loadFixture( + "classpath:fixtures/functional/controllers/authController/PostSalesforceConnectFixture.json", + currentFunctionName); + loadFixture.perform(fixtureData); + + List testDataItems = common.loadScenariosData( + "classpath:data/functional/controllers/authController/SalesforceConnect.scenarios.json", + currentFunctionName); + + for (Scenario testDataItem : testDataItems) { + logger.info("Running test scenario: " + testDataItem.getDescription()); + ObjectMapper objectMapper = new ObjectMapper(); + + HttpResponse getTokensMockRes = new HttpResponse(); + getTokensMockRes.setResponseBody(objectMapper.writeValueAsString(testDataItem.getMocks().get("getTokens"))); + + when(mockGetTokens.getTokens(anyString(), anyString())).thenReturn(getTokensMockRes); + + ResultActions resultActions = mockMvc.perform(MockMvcRequestBuilders.post("/api/v1/auth/salesforce/connect") + .content(objectMapper.writeValueAsString(testDataItem.getInput().get("body"))) + .accept(MediaType.APPLICATION_JSON) + .characterEncoding("UTF-8") + .contentType(MediaType.APPLICATION_JSON)); + + String actualOutput = resultActions.andReturn().getResponse().getContentAsString(); + + if (resultActions.andReturn().getResponse().getStatus() == 200) { + assertEquals(objectMapper.writeValueAsString(testDataItem.getOutput()), actualOutput); + verify(mockGetTokens, times(1)).getTokens(anyString(), anyString()); + verify(mockGetIdentity, times(0)).getUserIdentity(anyString(), anyString()); + } + else if (resultActions.andReturn().getResponse().getStatus() == 400) { + common.compareErrors(testDataItem, actualOutput); + } + else { + assertEquals(testDataItem.getOutput().get("http_code"), + resultActions.andReturn().getResponse().getStatus()); + } + } + + } + + @Test + public void testPostSalesforceConnectDisconnectedUserSignup() throws Exception { + String currentFunctionName = new Object() { + }.getClass().getEnclosingMethod().getName(); + + FixtureData fixtureData = common.loadFixture( + "classpath:fixtures/functional/controllers/authController/PostSalesforceConnectFixture.json", + currentFunctionName); + loadFixture.perform(fixtureData); + + List testDataItems = common.loadScenariosData( + "classpath:data/functional/controllers/authController/SalesforceConnect.scenarios.json", + currentFunctionName); + for (Scenario testDataItem : testDataItems) { + ObjectMapper objectMapper = new ObjectMapper(); + + HttpResponse getTokensMockRes = new HttpResponse(); + getTokensMockRes.setResponseBody(objectMapper.writeValueAsString(testDataItem.getMocks().get("getTokens"))); + + HttpResponse getIdentityMockRes = new HttpResponse(); + getIdentityMockRes + .setResponseBody(objectMapper.writeValueAsString(testDataItem.getMocks().get("getIdentity"))); + + when(mockGetTokens.getTokens(anyString(), anyString())).thenReturn(getTokensMockRes); + when(mockGetIdentity.getUserIdentity(anyString(), anyString())).thenReturn(getIdentityMockRes); + + ResultActions resultActions = mockMvc.perform(MockMvcRequestBuilders.post("/api/v1/auth/salesforce/connect") + .content(objectMapper.writeValueAsString(testDataItem.getInput().get("body"))) + .accept(MediaType.APPLICATION_JSON) + .characterEncoding("UTF-8") + .contentType(MediaType.APPLICATION_JSON)); + + String actualOutput = resultActions.andReturn().getResponse().getContentAsString(); + + if (resultActions.andReturn().getResponse().getStatus() == 200) { + assertEquals(objectMapper.writeValueAsString(testDataItem.getOutput()), actualOutput); + } + else if (resultActions.andReturn().getResponse().getStatus() == 400) { + common.compareErrors(testDataItem, actualOutput); + } + else { + assertEquals(testDataItem.getOutput().get("http_code"), + resultActions.andReturn().getResponse().getStatus()); + } + } + } + +} \ No newline at end of file diff --git a/src/test/java/com/salessparrow/api/functional/controllers/crmOrganizationUserController/GetCrmOrganizationUserListTest.java b/src/test/java/com/salessparrow/api/functional/controllers/crmOrganizationUserController/GetCrmOrganizationUserListTest.java new file mode 100644 index 00000000..4c439e11 --- /dev/null +++ b/src/test/java/com/salessparrow/api/functional/controllers/crmOrganizationUserController/GetCrmOrganizationUserListTest.java @@ -0,0 +1,138 @@ +package com.salessparrow.api.functional.controllers.crmOrganizationUserController; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +import java.io.IOException; +import java.util.List; +import java.util.stream.Stream; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; +import org.springframework.core.io.DefaultResourceLoader; +import org.springframework.core.io.Resource; +import org.springframework.http.MediaType; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.dynamobee.exception.DynamobeeException; +import com.salessparrow.api.helper.Cleanup; +import com.salessparrow.api.helper.Common; +import com.salessparrow.api.helper.Constants; +import com.salessparrow.api.helper.FixtureData; +import com.salessparrow.api.helper.LoadFixture; +import com.salessparrow.api.helper.Scenario; +import com.salessparrow.api.helper.Setup; +import com.salessparrow.api.lib.globalConstants.CookieConstants; +import com.salessparrow.api.lib.httpLib.HttpClient.HttpResponse; +import com.salessparrow.api.lib.salesforce.helper.MakeCompositeRequest; + +import jakarta.servlet.http.Cookie; + +@SpringBootTest +@AutoConfigureMockMvc +@WebAppConfiguration +@Import({ Setup.class, Cleanup.class, Common.class, LoadFixture.class }) +public class GetCrmOrganizationUserListTest { + + @Autowired + private MockMvc mockMvc; + + @Autowired + private Common common; + + @Autowired + private LoadFixture loadFixture; + + @Autowired + private Setup setup; + + @Autowired + private Cleanup cleanup; + + @MockBean + private MakeCompositeRequest makeCompositeRequestMock; + + @BeforeEach + public void setUp() throws DynamobeeException { + setup.perform(); + } + + @AfterEach + public void tearDown() { + cleanup.perform(); + } + + @ParameterizedTest + @MethodSource("testScenariosProvider") + public void getCrmOrganizationUserList(Scenario testScenario) throws Exception { + // Load fixture data + String currentFunctionName = new Object() { + }.getClass().getEnclosingMethod().getName(); + FixtureData fixtureData = common.loadFixture( + "classpath:fixtures/functional/controllers/crmOrganizationUserController/getCrmOrganizationUserList.fixtures.json", + currentFunctionName); + loadFixture.perform(fixtureData); + + // Read data from the scenario + ObjectMapper objectMapper = new ObjectMapper(); + + String cookieValue = Constants.SALESFORCE_ACTIVE_USER_COOKIE_VALUE; + String q = (String) testScenario.getInput().get("q"); + + // Prepare mock responses + HttpResponse getAccountMockResponse = new HttpResponse(); + getAccountMockResponse + .setResponseBody(objectMapper.writeValueAsString(testScenario.getMocks().get("makeCompositeRequest"))); + when(makeCompositeRequestMock.makePostRequest(any(), any())).thenReturn(getAccountMockResponse); + + // Perform the request + String url = "/api/v1/crm-organization-users"; + + ResultActions resultActions = mockMvc.perform(MockMvcRequestBuilders.get(url) + .cookie(new Cookie(CookieConstants.USER_LOGIN_COOKIE_NAME, cookieValue)) + .param("q", q) + .contentType(MediaType.APPLICATION_JSON)); + + // Check the response + String expectedOutput = objectMapper.writeValueAsString(testScenario.getOutput()); + String actualOutput = resultActions.andReturn().getResponse().getContentAsString(); + + JsonNode expectedOutputJson = objectMapper.readTree(expectedOutput); + JsonNode actualOutputJson = objectMapper.readTree(actualOutput); + + if (resultActions.andReturn().getResponse().getStatus() == 200) { + assertEquals(expectedOutputJson, actualOutputJson); + } + else { + common.compareErrors(testScenario, actualOutput); + } + } + + static Stream testScenariosProvider() throws IOException { + List testScenarios = loadScenarios(); + return testScenarios.stream(); + } + + private static List loadScenarios() throws IOException { + String scenariosPath = "classpath:data/functional/controllers/crmOrganizationUserController/getCrmOrganizationUserList.scenarios.json"; + Resource resource = new DefaultResourceLoader().getResource(scenariosPath); + ObjectMapper objectMapper = new ObjectMapper(); + return objectMapper.readValue(resource.getInputStream(), new TypeReference>() { + }); + } + +} diff --git a/src/test/java/com/salessparrow/api/functional/controllers/suggestionsController/PostCrmActionsSuggestionsTest.java b/src/test/java/com/salessparrow/api/functional/controllers/suggestionsController/PostCrmActionsSuggestionsTest.java new file mode 100644 index 00000000..73abe853 --- /dev/null +++ b/src/test/java/com/salessparrow/api/functional/controllers/suggestionsController/PostCrmActionsSuggestionsTest.java @@ -0,0 +1,109 @@ +package com.salessparrow.api.functional.controllers.suggestionsController; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +import java.io.IOException; +import java.util.List; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; +import org.springframework.http.MediaType; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.dynamobee.exception.DynamobeeException; +import com.salessparrow.api.helper.Cleanup; +import com.salessparrow.api.helper.Common; +import com.salessparrow.api.helper.Constants; +import com.salessparrow.api.helper.FixtureData; +import com.salessparrow.api.helper.LoadFixture; +import com.salessparrow.api.helper.Scenario; +import com.salessparrow.api.helper.Setup; +import com.salessparrow.api.lib.globalConstants.CookieConstants; +import com.salessparrow.api.lib.httpLib.HttpClient.HttpResponse; +import com.salessparrow.api.lib.openAi.OpenAiRequest; + +import jakarta.servlet.http.Cookie; + +@SpringBootTest +@AutoConfigureMockMvc +@WebAppConfiguration +@Import({ Setup.class, Cleanup.class, Common.class, LoadFixture.class }) +public class PostCrmActionsSuggestionsTest { + + @Autowired + private MockMvc mockMvc; + + @Autowired + private Setup setup; + + @Autowired + private Cleanup cleanup; + + @Autowired + private Common common; + + @Autowired + private LoadFixture loadFixture; + + @MockBean + private OpenAiRequest openAiRequestMock; + + @BeforeEach + public void setUp() throws DynamobeeException, IOException { + setup.perform(); + } + + @AfterEach + public void tearDown() { + cleanup.perform(); + } + + @Test + public void testPostCrmActionsSuggestions() throws Exception { + String currentFunctionName = new Object() { + }.getClass().getEnclosingMethod().getName(); + FixtureData fixtureData = common.loadFixture( + "classpath:fixtures/functional/controllers/suggestionsController/postCrmActionsSuggestions.fixtures.json", + currentFunctionName); + loadFixture.perform(fixtureData); + + List testDataItems = common.loadScenariosData( + "classpath:data/functional/controllers/suggestionsController/crmActionsSuggestions.scenarios.json"); + for (Scenario testDataItem : testDataItems) { + ObjectMapper objectMapper = new ObjectMapper(); + HttpResponse getAccountMockResponse = new HttpResponse(); + getAccountMockResponse + .setResponseBody(objectMapper.writeValueAsString(testDataItem.getMocks().get("makeRequest"))); + when(openAiRequestMock.makeRequest(any())).thenReturn(getAccountMockResponse); + + String expectedOutput = objectMapper.writeValueAsString(testDataItem.getOutput()); + String cookieValue = Constants.SALESFORCE_ACTIVE_USER_COOKIE_VALUE; + + ResultActions resultActions = mockMvc.perform(MockMvcRequestBuilders.post("/api/v1/suggestions/crm-actions") + .content(objectMapper.writeValueAsString(testDataItem.getInput())) + .cookie(new Cookie(CookieConstants.USER_LOGIN_COOKIE_NAME, cookieValue)) + .contentType(MediaType.APPLICATION_JSON)); + + String actualOutput = resultActions.andReturn().getResponse().getContentAsString(); + if (resultActions.andReturn().getResponse().getStatus() == 200) { + assertEquals(expectedOutput, actualOutput); + } + else { + common.compareErrors(testDataItem, actualOutput); + } + } + } + +} diff --git a/src/test/java/com/salessparrow/api/functional/controllers/userController/GetCurrentUserTest.java b/src/test/java/com/salessparrow/api/functional/controllers/userController/GetCurrentUserTest.java index f14fdcba..9d022e18 100644 --- a/src/test/java/com/salessparrow/api/functional/controllers/userController/GetCurrentUserTest.java +++ b/src/test/java/com/salessparrow/api/functional/controllers/userController/GetCurrentUserTest.java @@ -33,54 +33,60 @@ @WebAppConfiguration @Import({ Setup.class, Cleanup.class, Common.class, LoadFixture.class }) public class GetCurrentUserTest { - @Autowired - private MockMvc mockMvc; - - @Autowired - private Setup setup; - - @Autowired - private Cleanup cleanup; - - @Autowired - private Common common; - - @Autowired - private LoadFixture loadFixture; - - @BeforeEach - public void setUp() throws DynamobeeException, IOException { - setup.perform(); - } - - @AfterEach - public void tearDown() { - cleanup.perform(); - } - - @Test - public void getCurrentUser() throws Exception{ - String currentFunctionName = new Object(){}.getClass().getEnclosingMethod().getName(); - FixtureData fixtureData = common.loadFixture("classpath:fixtures/controllers/userController/getCurrentUser.fixtures.json", - currentFunctionName); - loadFixture.perform(fixtureData); - - List testDataItems = common.loadScenariosData("classpath:data/controllers/userController/getCurrentUser.scenarios.json"); - for (Scenario testDataItem : testDataItems) { - ObjectMapper objectMapper = new ObjectMapper(); - String expectedOutput = objectMapper.writeValueAsString(testDataItem.getOutput()); - String cookieValue = (String) testDataItem.getInput().get("cookie"); - - ResultActions resultActions = mockMvc.perform(MockMvcRequestBuilders.get("/api/v1/users/current") - .cookie(new Cookie(CookieConstants.USER_LOGIN_COOKIE_NAME, cookieValue)) - .contentType(MediaType.APPLICATION_JSON)); - - String actualOutput = resultActions.andReturn().getResponse().getContentAsString(); - if(resultActions.andReturn().getResponse().getStatus() == 200) { - assertEquals(expectedOutput, actualOutput); - } else { - common.compareErrors(testDataItem, actualOutput); - } - } - } + + @Autowired + private MockMvc mockMvc; + + @Autowired + private Setup setup; + + @Autowired + private Cleanup cleanup; + + @Autowired + private Common common; + + @Autowired + private LoadFixture loadFixture; + + @BeforeEach + public void setUp() throws DynamobeeException, IOException { + setup.perform(); + } + + @AfterEach + public void tearDown() { + cleanup.perform(); + } + + @Test + public void getCurrentUser() throws Exception { + String currentFunctionName = new Object() { + }.getClass().getEnclosingMethod().getName(); + FixtureData fixtureData = common.loadFixture( + "classpath:fixtures/functional/controllers/userController/getCurrentUser.fixtures.json", + currentFunctionName); + loadFixture.perform(fixtureData); + + List testDataItems = common + .loadScenariosData("classpath:data/functional/controllers/userController/getCurrentUser.scenarios.json"); + for (Scenario testDataItem : testDataItems) { + ObjectMapper objectMapper = new ObjectMapper(); + String expectedOutput = objectMapper.writeValueAsString(testDataItem.getOutput()); + String cookieValue = (String) testDataItem.getInput().get("cookie"); + + ResultActions resultActions = mockMvc.perform(MockMvcRequestBuilders.get("/api/v1/users/current") + .cookie(new Cookie(CookieConstants.USER_LOGIN_COOKIE_NAME, cookieValue)) + .contentType(MediaType.APPLICATION_JSON)); + + String actualOutput = resultActions.andReturn().getResponse().getContentAsString(); + if (resultActions.andReturn().getResponse().getStatus() == 200) { + assertEquals(expectedOutput, actualOutput); + } + else { + common.compareErrors(testDataItem, actualOutput); + } + } + } + } diff --git a/src/test/java/com/salessparrow/api/functional/exception/GlobalExceptionHandlerTest.java b/src/test/java/com/salessparrow/api/functional/exception/GlobalExceptionHandlerTest.java new file mode 100644 index 00000000..a1f759f0 --- /dev/null +++ b/src/test/java/com/salessparrow/api/functional/exception/GlobalExceptionHandlerTest.java @@ -0,0 +1,71 @@ +package com.salessparrow.api.functional.exception; + +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.IOException; +import java.util.List; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.Import; +import org.springframework.http.MediaType; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.dynamobee.exception.DynamobeeException; +import com.salessparrow.api.helper.Cleanup; +import com.salessparrow.api.helper.Common; +import com.salessparrow.api.helper.LoadFixture; +import com.salessparrow.api.helper.Scenario; +import com.salessparrow.api.helper.Setup; + +@SpringBootTest +@AutoConfigureMockMvc +@WebAppConfiguration +@Import({ Setup.class, Cleanup.class, Common.class, LoadFixture.class }) +public class GlobalExceptionHandlerTest { + + @Autowired + private MockMvc mockMvc; + + @Autowired + private Setup setup; + + @Autowired + private Cleanup cleanup; + + @Autowired + private Common common; + + @BeforeEach + public void setUp() throws DynamobeeException, IOException { + setup.perform(); + } + + @AfterEach + public void tearDown() { + cleanup.perform(); + } + + @Test + public void handleNoHandlerFoundException() throws Exception { + List testDataItems = common + .loadScenariosData("classpath:data/functional/exceptions/globalExceptionHandler.scenarios.json"); + for (Scenario testDataItem : testDataItems) { + + ResultActions resultActions = mockMvc + .perform(MockMvcRequestBuilders.get("/api/v1/unknown-route").contentType(MediaType.APPLICATION_JSON)); + + String contentAsString = resultActions.andReturn().getResponse().getContentAsString(); + common.compareErrors(testDataItem, contentAsString); + } + + } + +} diff --git a/src/test/java/com/salessparrow/api/functional/interceptors/JsonOnlyInterceptorTest.java b/src/test/java/com/salessparrow/api/functional/interceptors/JsonOnlyInterceptorTest.java new file mode 100644 index 00000000..94d090fe --- /dev/null +++ b/src/test/java/com/salessparrow/api/functional/interceptors/JsonOnlyInterceptorTest.java @@ -0,0 +1,71 @@ +package com.salessparrow.api.functional.interceptors; + +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.IOException; +import java.util.List; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.Import; +import org.springframework.http.MediaType; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.dynamobee.exception.DynamobeeException; +import com.salessparrow.api.helper.Cleanup; +import com.salessparrow.api.helper.Common; +import com.salessparrow.api.helper.LoadFixture; +import com.salessparrow.api.helper.Scenario; +import com.salessparrow.api.helper.Setup; + +@SpringBootTest +@AutoConfigureMockMvc +@WebAppConfiguration +@Import({ Setup.class, Cleanup.class, Common.class, LoadFixture.class }) +public class JsonOnlyInterceptorTest { + + @Autowired + private MockMvc mockMvc; + + @Autowired + private Setup setup; + + @Autowired + private Cleanup cleanup; + + @Autowired + private Common common; + + @BeforeEach + public void setUp() throws DynamobeeException, IOException { + setup.perform(); + } + + @AfterEach + public void tearDown() { + cleanup.perform(); + } + + @Test + public void preHandle() throws Exception { + List testDataItems = common + .loadScenariosData("classpath:data/functional/interceptors/jsonOnlyInterceptor.scenarios.json"); + for (Scenario testDataItem : testDataItems) { + ResultActions resultActions = mockMvc + .perform(MockMvcRequestBuilders.get("/api/v1/auth/salesforce/redirect-url") + .contentType(MediaType.ALL_VALUE)); + + String contentAsString = resultActions.andReturn().getResponse().getContentAsString(); + common.compareErrors(testDataItem, contentAsString); + } + + } + +} diff --git a/src/test/java/com/salessparrow/api/helper/Cleanup.java b/src/test/java/com/salessparrow/api/helper/Cleanup.java index 86125963..156c0b40 100644 --- a/src/test/java/com/salessparrow/api/helper/Cleanup.java +++ b/src/test/java/com/salessparrow/api/helper/Cleanup.java @@ -4,6 +4,7 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.cache.CacheManager; /** * This class is used to clean the setup after each test. @@ -11,34 +12,39 @@ @TestConfiguration public class Cleanup { - Logger logger = LoggerFactory.getLogger(Cleanup.class); - - @Autowired - private DropTables dropTables; - - /** - * Clean the setup after each test. - * - Flush the cache - * - Drop dynamodb tables - */ - public void perform() { - logger.info("Cleaning setup"); - - flushCache(); - dropTables(); - } - - /** - * Flush the cache. - */ - private void flushCache(){ - logger.info("Cleanup: Flushing cache"); - } - - /** - * Drop the tables. - */ - private void dropTables() { - dropTables.perform(); - } + Logger logger = LoggerFactory.getLogger(Cleanup.class); + + @Autowired + private DropTables dropTables; + + @Autowired + private CacheManager cacheManager; + + /** + * Clean the setup after each test. - Flush the cache - Drop dynamodb tables + */ + public void perform() { + logger.info("Cleaning setup"); + + flushCache(); + dropTables(); + } + + /** + * Flush the cache. + */ + private void flushCache() { + logger.info("Setup: Flushing cache"); + cacheManager.getCacheNames().stream().forEach(cacheName -> { + cacheManager.getCache(cacheName).clear(); + }); + } + + /** + * Drop the tables. + */ + private void dropTables() { + dropTables.perform(); + } + } diff --git a/src/test/java/com/salessparrow/api/helper/Common.java b/src/test/java/com/salessparrow/api/helper/Common.java index ed8a65ff..5a0cd9a7 100644 --- a/src/test/java/com/salessparrow/api/helper/Common.java +++ b/src/test/java/com/salessparrow/api/helper/Common.java @@ -21,61 +21,88 @@ */ @TestConfiguration public class Common { - @Autowired - private ResourceLoader resourceLoader; - - /** - * Load the fixture data from the given location. - * - * @param location - * @param key - * @return FixtureData - * @throws IOException - */ - public FixtureData loadFixture(String location, String key) throws IOException { - Resource resource = resourceLoader.getResource(location); - ObjectMapper objectMapper = new ObjectMapper(); - Map fixtureMap = new HashMap<>(); - fixtureMap = objectMapper.readValue(resource.getInputStream(), new TypeReference>() { - }); - return fixtureMap.get(key); - } - - /** - * Load the test scenario data from the given location. - * - * @param location - * @return List - * @throws IOException - */ - public List loadScenariosData(String location) throws IOException { - Resource resource = resourceLoader.getResource(location); - ObjectMapper objectMapper = new ObjectMapper(); - return objectMapper.readValue(resource.getInputStream(), new TypeReference>() { - }); - } - - /** - * Compare the errors from the test data with the actual errors. - * - * @param testDataItem - * @param contentAsString - */ - public void compareErrors(Scenario testDataItem, String contentAsString) throws Exception { - ObjectMapper objectMapper = new ObjectMapper(); - JsonNode resJsonNode = objectMapper.readTree(contentAsString); - Integer httpCode = resJsonNode.get("http_code").asInt(); - assertEquals(testDataItem.getOutput().get("http_code"), httpCode); - - String code = resJsonNode.get("code").asText(); - assertEquals(testDataItem.getOutput().get("code"), code); - - List paramErrors = (List) testDataItem.getOutput().get("param_errors"); - if (paramErrors != null) { - for (int i = 0; i < paramErrors.size(); i++) { - String actualError = resJsonNode.get("param_errors").get(i).get("param_error_identifier").asText(); - assertEquals(paramErrors.contains(actualError), true); - } - } - } -} + + @Autowired + private ResourceLoader resourceLoader; + + /** + * Load the fixture data from the given location. + * @param location + * @param key + * @return FixtureData + * @throws IOException + */ + public FixtureData loadFixture(String location, String key) throws IOException { + Resource resource = resourceLoader.getResource(location); + ObjectMapper objectMapper = new ObjectMapper(); + Map fixtureMap = new HashMap<>(); + fixtureMap = objectMapper.readValue(resource.getInputStream(), + new TypeReference>() { + }); + return fixtureMap.get(key); + } + + /** + * Load the test scenario data from the given location. + * @param location + * @return List + * @throws IOException + */ + public List loadScenariosData(String location) throws IOException { + Resource resource = resourceLoader.getResource(location); + ObjectMapper objectMapper = new ObjectMapper(); + return objectMapper.readValue(resource.getInputStream(), new TypeReference>() { + }); + } + + /** + * Load the test scenario data from the given location and key. + * @param location - location of the test data + * @param key - key of the test data + * @return List + * @throws IOException + */ + public List loadScenariosData(String location, String key) throws IOException { + Resource resource = resourceLoader.getResource(location); + ObjectMapper objectMapper = new ObjectMapper(); + Map> scenarioMap = new HashMap<>(); + scenarioMap = objectMapper.readValue(resource.getInputStream(), + new TypeReference>>() { + }); + return scenarioMap.get(key); + } + + /** + * Compare the errors from the test data with the actual errors. + * @param testDataItem + * @param contentAsString + */ + public void compareErrors(Scenario testDataItem, String contentAsString) throws Exception { + ObjectMapper objectMapper = new ObjectMapper(); + JsonNode resJsonNode = objectMapper.readTree(contentAsString); + Integer httpCode = resJsonNode.get("http_code").asInt(); + assertEquals(testDataItem.getOutput().get("http_code"), httpCode); + + String code = resJsonNode.get("code").asText(); + assertEquals(testDataItem.getOutput().get("code"), code); + + List paramErrors = (List) testDataItem.getOutput().get("param_errors"); + if (paramErrors != null) { + for (int i = 0; i < paramErrors.size(); i++) { + String actualError = resJsonNode.get("param_errors").get(i).get("param_error_identifier").asText(); + assertEquals(paramErrors.contains(actualError), true); + } + } + } + + // Custom assertion method that treats {} and "" as the same + public void assertCustomEquals(String expected, String actual) { + + if (expected.equals("{}")) { + expected = ""; + } + + assertEquals(expected, actual); + } + +} \ No newline at end of file diff --git a/src/test/java/com/salessparrow/api/helper/Constants.java b/src/test/java/com/salessparrow/api/helper/Constants.java index 2d52bcd8..0ad4b468 100644 --- a/src/test/java/com/salessparrow/api/helper/Constants.java +++ b/src/test/java/com/salessparrow/api/helper/Constants.java @@ -4,5 +4,7 @@ * Constants is a helper class for the tests. */ public class Constants { - public static final String SALESFORCE_ACTIVE_USET_COOKIE_VALUE = "1:00112233445566AAA1:SALESFORCE:1899000000:IbFOqqub193hajGqMDwrF41iUiKl8KJKWpmPl7oqX4eesrydP9cSx483f86OFK+dtSNAukoy+Tr7pRYsD3yVNw=="; + + public static final String SALESFORCE_ACTIVE_USER_COOKIE_VALUE = "1:0055i00000AUxQHAA1:SALESFORCE:app:1692878227:a2xOxjcPChK/YsKCWRuY50J3gH21/tqcGlv7sWU/aPw3/UFVFPbvWn2LLzY5B5kVPXjj1gELRqa4B4ds6uuqIw=="; + } diff --git a/src/test/java/com/salessparrow/api/helper/DropTables.java b/src/test/java/com/salessparrow/api/helper/DropTables.java index b9ccad92..4b7da5e3 100644 --- a/src/test/java/com/salessparrow/api/helper/DropTables.java +++ b/src/test/java/com/salessparrow/api/helper/DropTables.java @@ -17,38 +17,39 @@ */ @TestConfiguration public class DropTables { - Logger logger = LoggerFactory.getLogger(DropTables.class); - - @Autowired - private AmazonDynamoDB dynamoDB; - - /** - * This method is used to drop the tables. - */ - public void perform() { - if (!CoreConstants.environment().equals("test")) { - throw new RuntimeException("Cannot drop tables in non test environment"); - } - - List tableList = getAllTableList(); - String envPrefix = CoreConstants.environment() + "_"; - - for (String tableName : tableList) { - if (tableName.startsWith(envPrefix)) { - dynamoDB.deleteTable(tableName); - } - } - } - - /** - * This method is used to get all the table list. - * - * @return List of table names - */ - private List getAllTableList() { - ListTablesRequest listTablesRequest = new ListTablesRequest(); - ListTablesResult listTablesResult = dynamoDB.listTables(listTablesRequest); - - return listTablesResult.getTableNames(); - } + + Logger logger = LoggerFactory.getLogger(DropTables.class); + + @Autowired + private AmazonDynamoDB dynamoDB; + + /** + * This method is used to drop the tables. + */ + public void perform() { + if (!CoreConstants.isTestEnvironment() && !CoreConstants.isLocalTestEnvironment()) { + throw new RuntimeException("Cannot drop tables in non test environment"); + } + + List tableList = getAllTableList(); + String envPrefix = CoreConstants.environment() + "_"; + + for (String tableName : tableList) { + if (tableName.startsWith(envPrefix)) { + dynamoDB.deleteTable(tableName); + } + } + } + + /** + * This method is used to get all the table list. + * @return List of table names + */ + private List getAllTableList() { + ListTablesRequest listTablesRequest = new ListTablesRequest(); + ListTablesResult listTablesResult = dynamoDB.listTables(listTablesRequest); + + return listTablesResult.getTableNames(); + } + } diff --git a/src/test/java/com/salessparrow/api/helper/FixtureData.java b/src/test/java/com/salessparrow/api/helper/FixtureData.java index d65283f4..f82bcfc8 100644 --- a/src/test/java/com/salessparrow/api/helper/FixtureData.java +++ b/src/test/java/com/salessparrow/api/helper/FixtureData.java @@ -6,12 +6,18 @@ @Data public class FixtureData { - List salesforce_users; - List salesforce_oauth_tokens; - List salesforce_organizations; + + List salesforce_users; + + List salesforce_oauth_tokens; + + List salesforce_organizations; + } @Data class FilePathData { - String filepath; + + String filepath; + } \ No newline at end of file diff --git a/src/test/java/com/salessparrow/api/helper/LoadFixture.java b/src/test/java/com/salessparrow/api/helper/LoadFixture.java index 151dfc2d..18d5f0ea 100644 --- a/src/test/java/com/salessparrow/api/helper/LoadFixture.java +++ b/src/test/java/com/salessparrow/api/helper/LoadFixture.java @@ -1,107 +1,108 @@ package com.salessparrow.api.helper; import java.io.IOException; +import java.io.InputStream; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; +import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import com.salessparrow.api.domain.SalesforceOauthToken; import com.salessparrow.api.domain.SalesforceOrganization; import com.salessparrow.api.domain.SalesforceUser; -import com.salessparrow.api.repositories.SalesforceOauthTokenRepository; -import com.salessparrow.api.repositories.SalesforceOrganizationRepository; -import com.salessparrow.api.repositories.SalesforceUserRepository; /** * LoadFixture is a helper class for the tests. */ public class LoadFixture { - @Autowired - private ResourceLoader resourceLoader; - - @Autowired - private SalesforceUserRepository salesforceUserRepository; - - @Autowired - private SalesforceOauthTokenRepository salesforceOauthTokenRepository; - - @Autowired - private SalesforceOrganizationRepository salesforceOrganizationRepository; - - /** - * Load the fixture data. - * - * @param location - * @return FixtureData - * @throws IOException - */ - public void perform(FixtureData fixtureData) throws IOException { - - if (fixtureData.getSalesforce_users() != null) { - for (FilePathData filePathData : fixtureData.getSalesforce_users()) { - SalesforceUser salesforceUser = loadSalesForceUserFixture(filePathData.getFilepath()); - salesforceUserRepository.saveSalesforceUser(salesforceUser); - } - } - - if (fixtureData.getSalesforce_oauth_tokens() != null) { - for (FilePathData filePathData : fixtureData.getSalesforce_oauth_tokens()) { - SalesforceOauthToken salesforceOauth = loadSalesForceOAuthTokenFixture(filePathData.getFilepath()); - salesforceOauthTokenRepository.saveSalesforceOauthToken(salesforceOauth); - } - } - - if (fixtureData.getSalesforce_organizations() != null) { - for (FilePathData filePathData : fixtureData.getSalesforce_organizations()) { - SalesforceOrganization salesforceOrganization = loadSalesForceOrganizationFixture(filePathData.getFilepath()); - salesforceOrganizationRepository.saveSalesforceOrganization(salesforceOrganization); - } - } - - } - - /** - * Load the SalesforceUser fixture data from the given location. - * - * @param location - * @return FixtureData - * @throws IOException - */ - public SalesforceUser loadSalesForceUserFixture(String location) throws IOException { - Resource resource = resourceLoader.getResource(location); - ObjectMapper objectMapper = new ObjectMapper(); - return objectMapper.readValue(resource.getInputStream(), new TypeReference() { - }); - } - - /** - * Load the SalesforceOauthToken fixture data from the given location. - * - * @param location - * @return FixtureData - * @throws IOException - */ - public SalesforceOauthToken loadSalesForceOAuthTokenFixture(String location) throws IOException { - Resource resource = resourceLoader.getResource(location); - ObjectMapper objectMapper = new ObjectMapper(); - return objectMapper.readValue(resource.getInputStream(), new TypeReference() { - }); - } - - /** - * Load the SalesforceOrganization fixture data from the given location. - * - * @param location - * @return FixtureData - * @throws IOException - */ - public SalesforceOrganization loadSalesForceOrganizationFixture(String location) throws IOException { - Resource resource = resourceLoader.getResource(location); - ObjectMapper objectMapper = new ObjectMapper(); - return objectMapper.readValue(resource.getInputStream(), new TypeReference() { - }); - } + + @Autowired + private ResourceLoader resourceLoader; + + @Autowired + private DynamoDBMapper dynamoDBMapper; + + /** + * Load the fixture data. + * @param location + * @return FixtureData + * @throws IOException + */ + public void perform(FixtureData fixtureData) throws IOException { + + if (fixtureData.getSalesforce_users() != null) { + for (FilePathData filePathData : fixtureData.getSalesforce_users()) { + SalesforceUser salesforceUser = loadSalesForceUserFixture(filePathData.getFilepath()); + dynamoDBMapper.save(salesforceUser); + } + } + + if (fixtureData.getSalesforce_oauth_tokens() != null) { + for (FilePathData filePathData : fixtureData.getSalesforce_oauth_tokens()) { + SalesforceOauthToken salesforceOauth = loadSalesForceOAuthTokenFixture(filePathData.getFilepath()); + dynamoDBMapper.save(salesforceOauth); + } + } + + if (fixtureData.getSalesforce_organizations() != null) { + for (FilePathData filePathData : fixtureData.getSalesforce_organizations()) { + SalesforceOrganization salesforceOrganization = loadSalesForceOrganizationFixture( + filePathData.getFilepath()); + dynamoDBMapper.save(salesforceOrganization); + } + } + + } + + /** + * Load the SalesforceUser fixture data from the given location. + * @param location + * @return FixtureData + * @throws IOException + */ + public SalesforceUser loadSalesForceUserFixture(String location) throws IOException { + Resource resource = resourceLoader.getResource(location); + ObjectMapper objectMapper = new ObjectMapper(); + + try (InputStream inputStream = resource.getInputStream()) { + return objectMapper.readValue(resource.getInputStream(), new TypeReference() { + }); + } + } + + /** + * Load the SalesforceOauthToken fixture data from the given location. + * @param location + * @return FixtureData + * @throws IOException + */ + public SalesforceOauthToken loadSalesForceOAuthTokenFixture(String location) throws IOException { + Resource resource = resourceLoader.getResource(location); + ObjectMapper objectMapper = new ObjectMapper(); + + try (InputStream inputStream = resource.getInputStream()) { + return objectMapper.readValue(resource.getInputStream(), new TypeReference() { + }); + } + } + + /** + * Load the SalesforceOrganization fixture data from the given location. + * @param location + * @return FixtureData + * @throws IOException + */ + public SalesforceOrganization loadSalesForceOrganizationFixture(String location) throws IOException { + Resource resource = resourceLoader.getResource(location); + ObjectMapper objectMapper = new ObjectMapper(); + + try (InputStream inputStream = resource.getInputStream()) { + return objectMapper.readValue(resource.getInputStream(), new TypeReference() { + }); + } + } + } diff --git a/src/test/java/com/salessparrow/api/helper/Scenario.java b/src/test/java/com/salessparrow/api/helper/Scenario.java index f7264683..f00d367d 100644 --- a/src/test/java/com/salessparrow/api/helper/Scenario.java +++ b/src/test/java/com/salessparrow/api/helper/Scenario.java @@ -6,11 +6,13 @@ @Data public class Scenario { - String description; - Map input; + String description; - Map mocks; + Map input; + + Map mocks; + + Map output; - Map output; } diff --git a/src/test/java/com/salessparrow/api/helper/Setup.java b/src/test/java/com/salessparrow/api/helper/Setup.java index c7df5374..cdaad0d5 100644 --- a/src/test/java/com/salessparrow/api/helper/Setup.java +++ b/src/test/java/com/salessparrow/api/helper/Setup.java @@ -1,5 +1,6 @@ package com.salessparrow.api.helper; +import org.springframework.cache.CacheManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -16,48 +17,52 @@ @Import({ DropTables.class }) public class Setup { - Logger logger = LoggerFactory.getLogger(Setup.class); - - @Autowired - private DropTables dropTables; - - @Autowired - private Dynamobee dynamobee; - - /** - * Create the setup. - * - Flush the cache - * - Drop dynamodb tables - * - Run migrations - * @throws DynamobeeException - */ - public void perform() throws DynamobeeException { - logger.info("Creating setup"); - - flushCache(); - dropTables(); - runMigrations(); - } - - /** - * Flush the cache. - */ - private void flushCache(){ - logger.info("Setup: Flushing cache"); - } - - /** - * Drop dynamodb tables. - */ - private void dropTables() { - dropTables.perform(); - } - - /** - * Run migrations. - * @throws DynamobeeException - */ - public void runMigrations() throws DynamobeeException { - dynamobee.execute(); - } + Logger logger = LoggerFactory.getLogger(Setup.class); + + @Autowired + private DropTables dropTables; + + @Autowired + private Dynamobee dynamobee; + + @Autowired + private CacheManager cacheManager; + + /** + * Create the setup. - Flush the cache - Drop dynamodb tables - Run migrations + * @throws DynamobeeException + */ + public void perform() throws DynamobeeException { + logger.info("Creating setup"); + + flushCache(); + dropTables(); + runMigrations(); + } + + /** + * Flush the cache. + */ + private void flushCache() { + logger.info("Setup: Flushing cache"); + cacheManager.getCacheNames().stream().forEach(cacheName -> { + cacheManager.getCache(cacheName).clear(); + }); + } + + /** + * Drop dynamodb tables. + */ + private void dropTables() { + dropTables.perform(); + } + + /** + * Run migrations. + * @throws DynamobeeException + */ + public void runMigrations() throws DynamobeeException { + dynamobee.execute(); + } + } diff --git a/src/test/java/com/salessparrow/api/lib/UtilTest.java b/src/test/java/com/salessparrow/api/lib/UtilTest.java index d819f9ed..2c735d10 100644 --- a/src/test/java/com/salessparrow/api/lib/UtilTest.java +++ b/src/test/java/com/salessparrow/api/lib/UtilTest.java @@ -15,30 +15,31 @@ public class UtilTest { - @Mock - private ObjectMapper objectMapper; + @Mock + private ObjectMapper objectMapper; - @InjectMocks - private Util util; + @InjectMocks + private Util util; - @BeforeEach - public void setup() { - MockitoAnnotations.openMocks(this); - } + @BeforeEach + public void setup() { + MockitoAnnotations.openMocks(this); + } - @Test - public void testGetJsonNode_ValidJson() throws Exception { - String jsonString = "{\"key\": \"value\"}"; + @Test + public void testGetJsonNode_ValidJson() throws Exception { + String jsonString = "{\"key\": \"value\"}"; - JsonNode resultJsonNode = util.getJsonNode(jsonString); + JsonNode resultJsonNode = util.getJsonNode(jsonString); - assertEquals("value", resultJsonNode.get("key").asText()); - } + assertEquals("value", resultJsonNode.get("key").asText()); + } - @Test - public void testGetJsonNode_InvalidJson() throws Exception { - String invalidJsonString = "invalid json"; + @Test + public void testGetJsonNode_InvalidJson() throws Exception { + String invalidJsonString = "invalid json"; + + assertThrows(CustomException.class, () -> util.getJsonNode(invalidJsonString)); + } - assertThrows(CustomException.class, () -> util.getJsonNode(invalidJsonString)); - } } diff --git a/src/test/java/com/salessparrow/api/unit/filters/SanitizationFilterTest.java b/src/test/java/com/salessparrow/api/unit/filters/SanitizationFilterTest.java new file mode 100644 index 00000000..8283d320 --- /dev/null +++ b/src/test/java/com/salessparrow/api/unit/filters/SanitizationFilterTest.java @@ -0,0 +1,177 @@ +package com.salessparrow.api.unit.filters; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.*; + +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import com.salessparrow.api.filter.SanitizationFilter; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.StringReader; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; +import java.util.Vector; +import java.util.stream.Collectors; + +class SanitizationFilterTest { + + @Mock + private HttpServletRequest request; + + @Mock + private HttpServletResponse response; + + @Mock + private FilterChain filterChain; + + @Mock + private ServletRequest nonHttpRequest; + + @InjectMocks + private SanitizationFilter sanitizationFilter; + + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + } + + @Test + void testDoFilter() throws Exception { + // Arrange: Setup common request behaviors with unsafe body and params + when(request.getReader()).thenReturn( + new BufferedReader(new StringReader("")) + ); + when(request.getParameterMap()).thenReturn(mockedParamMap(true)); + + // Act: Pass through sanitization filter + sanitizationFilter.doFilter(request, response, filterChain); + + // Assert: Check that the filterChain's doFilter is called + ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(HttpServletRequest.class); + verify(filterChain).doFilter(requestCaptor.capture(), eq(response)); + HttpServletRequest sanitizedRequest = requestCaptor.getValue(); + + // Assert: Check that request body is sanitized + String sanitizedBody = getRequestBody(sanitizedRequest); + assertFalse(sanitizedBody.contains(""))); + sanitizationFilter.doFilter(request, response, filterChain); + + ArgumentCaptor requestCaptor = + ArgumentCaptor.forClass(HttpServletRequest.class); + verify(filterChain).doFilter(requestCaptor.capture(), eq(response)); + HttpServletRequest sanitizedRequest = requestCaptor.getValue(); + + String sanitizedBody = getRequestBody(sanitizedRequest); + assertFalse(sanitizedBody.contains("" }); + paramMap.put("param2", new String[] { "" }); + } + else { + paramMap.put("param1", new String[] { "safe_value1" }); + paramMap.put("param2", new String[] { "safe_value2" }); + } + return paramMap; + } + + public String getRequestBody(HttpServletRequest request) throws IOException { + BufferedReader reader = request.getReader(); + if (reader == null) { + return ""; + } + + return reader.lines().collect(Collectors.joining(System.lineSeparator())); + } + +} diff --git a/src/test/java/com/salessparrow/api/unit/globalConstants/SalesforceConstantsTest.java b/src/test/java/com/salessparrow/api/unit/globalConstants/SalesforceConstantsTest.java new file mode 100644 index 00000000..36f1a00a --- /dev/null +++ b/src/test/java/com/salessparrow/api/unit/globalConstants/SalesforceConstantsTest.java @@ -0,0 +1,85 @@ +package com.salessparrow.api.unit.globalConstants; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; +import com.salessparrow.api.lib.globalConstants.SalesforceConstants; + +@SpringBootTest +public class SalesforceConstantsTest { + + @Test + void testCompositeUrlPath() { + assertEquals("/services/data/v58.0/composite", new SalesforceConstants().compositeUrlPath()); + } + + @Test + void testQueryUrlPath() { + assertEquals("/services/data/v58.0/query/?q=", new SalesforceConstants().queryUrlPath()); + } + + @Test + void testSObjectsPath() { + assertEquals("/services/data/v58.0/sobjects", new SalesforceConstants().sObjectsPath()); + } + + @Test + void testSalesforceCompositeUrl() { + assertEquals("https://test.salesforce.com/services/data/v58.0/composite", + new SalesforceConstants().salesforceCompositeUrl("https://test.salesforce.com")); + } + + @Test + void testOauth2AuthorizeUrl() { + assertEquals("https://test.salesforce.com/services/oauth2/authorize", + new SalesforceConstants().oauth2AuthorizeUrl()); + } + + @Test + void testOauth2Url() { + assertEquals("https://test.salesforce.com/services/oauth2/token", new SalesforceConstants().oauth2Url()); + } + + @Test + void testSalesforceCreateNoteUrl() { + assertEquals("/services/data/v58.0/sobjects/ContentNote", new SalesforceConstants().salesforceCreateNoteUrl()); + } + + @Test + void testSalesforceAttachNoteUrl() { + assertEquals("/services/data/v58.0/sobjects/ContentDocumentLink", + new SalesforceConstants().salesforceAttachNoteUrl()); + } + + @Test + void testIdentityUrl() { + assertEquals("/services/oauth2/userinfo", new SalesforceConstants().identityUrl()); + } + + @Test + void testAuthorizationCodeGrantType() { + assertEquals("authorization_code", new SalesforceConstants().authorizationCodeGrantType()); + } + + @Test + void testRefreshTokenGrantType() { + assertEquals("refresh_token", new SalesforceConstants().refreshTokenGrantType()); + } + + @Test + void testTimeoutMillis() { + assertEquals(10000, new SalesforceConstants().timeoutMillis()); + } + + @Test + void testSalesforceNotesContentUrl() { + assertEquals("https://test.salesforce.com/services/data/v58.0/sobjects/ContentNote/123/Content", + new SalesforceConstants().salesforceNotesContentUrl("https://test.salesforce.com", "123")); + } + + @Test + void testSalesforceCreateTaskUrl() { + assertEquals("/services/data/v58.0/sobjects/Task", new SalesforceConstants().salesforceCreateTaskUrl()); + } + +} diff --git a/src/test/java/com/salessparrow/api/unit/globalConstants/UserConstantsTest.java b/src/test/java/com/salessparrow/api/unit/globalConstants/UserConstantsTest.java new file mode 100644 index 00000000..0b3a70ef --- /dev/null +++ b/src/test/java/com/salessparrow/api/unit/globalConstants/UserConstantsTest.java @@ -0,0 +1,27 @@ +package com.salessparrow.api.unit.globalConstants; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; +import com.salessparrow.api.lib.globalConstants.UserConstants; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +public class UserConstantsTest { + + @Test + void testGetName() { + assertEquals("SALESFORCE", UserConstants.SALESFORCE_USER_KIND); + } + + @Test + void testPrivateConstructor() throws NoSuchMethodException, IllegalAccessException, InstantiationException { + Constructor constructor = UserConstants.class.getDeclaredConstructor(); + constructor.setAccessible(true); + assertThrows(InvocationTargetException.class, constructor::newInstance); + } + +} diff --git a/src/test/java/com/salessparrow/api/unit/lib/UtilTest.java b/src/test/java/com/salessparrow/api/unit/lib/UtilTest.java new file mode 100644 index 00000000..5afc4cb1 --- /dev/null +++ b/src/test/java/com/salessparrow/api/unit/lib/UtilTest.java @@ -0,0 +1,109 @@ +package com.salessparrow.api.unit.lib; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.MockitoAnnotations; + +import com.fasterxml.jackson.databind.JsonNode; +import com.salessparrow.api.exception.CustomException; +import com.salessparrow.api.lib.Util; + +public class UtilTest { + + @InjectMocks + private Util util; + + @BeforeEach + public void setup() { + MockitoAnnotations.openMocks(this); + } + + @Test + public void testEncode() { + String plainText = "Hello, World!"; + String encodedText = Util.base64Encode(plainText); + assertEquals("SGVsbG8sIFdvcmxkIQ==", encodedText); + } + + @Test + public void testEncodeEmptyString() { + String plainText = ""; + String encodedText = Util.base64Encode(plainText); + assertEquals("", encodedText); + } + + @Test + public void testEncodeNullString() { + String plainText = null; + assertThrows(CustomException.class, () -> Util.base64Encode(plainText)); + } + + @Test + public void testDecode() { + String encodedText = "SGVsbG8sIFdvcmxkIQ=="; + String decodedText = Util.base64Decode(encodedText); + assertEquals("Hello, World!", decodedText); + } + + @Test + public void testDecodeEmptyString() { + String encodedText = ""; + String decodedText = Util.base64Decode(encodedText); + assertEquals("", decodedText); + } + + @Test + public void testDecodeNullString() { + String encodedText = null; + assertThrows(CustomException.class, () -> Util.base64Decode(encodedText)); + } + + @Test + public void testDecodeInvalidString() { + String encodedText = "SGVsbG8sIFdvcmxkIQ="; + assertThrows(CustomException.class, () -> Util.base64Decode(encodedText)); + } + + @Test + public void testGetJsonNode_ValidJson() throws Exception { + String jsonString = "{\"key\": \"value\"}"; + + JsonNode resultJsonNode = util.getJsonNode(jsonString); + + assertEquals("value", resultJsonNode.get("key").asText()); + } + + @Test + public void testGetJsonNode_InvalidJson() throws Exception { + String invalidJsonString = "invalid json"; + + assertThrows(CustomException.class, () -> util.getJsonNode(invalidJsonString)); + } + + @Test + public void testEscapeSpecialChars() { + String[] inputStrings = { "\\a", "%a", "_a", "'a", "\"a" }; + String[] expectedOutputs = { "\\\\a", "\\%a", "\\_a", "\\'a", "\\\"a" }; + + for (int i = 0; i < inputStrings.length; i++) { + String result = Util.escapeSpecialChars(inputStrings[i]); + assertEquals(expectedOutputs[i], result); + } + } + + @Test + public void testUrlEncoder() { + String[] inputStrings = { "\\\\a", "\\%a", "\\_a", "\\'a", "\\\"a", "a+b", "a b" }; + String[] expectedOutputs = { "%5C%5Ca", "%5C%25a", "%5C_a", "%5C%27a", "%5C%22a", "a%2Bb", "a+b" }; + + for (int i = 0; i < inputStrings.length; i++) { + String result = Util.urlEncoder(inputStrings[i]); + assertEquals(expectedOutputs[i], result); + } + } + +} diff --git a/src/test/java/com/salessparrow/api/unit/lib/openAi/OpenAiRequestTest.java b/src/test/java/com/salessparrow/api/unit/lib/openAi/OpenAiRequestTest.java new file mode 100644 index 00000000..37b6ec60 --- /dev/null +++ b/src/test/java/com/salessparrow/api/unit/lib/openAi/OpenAiRequestTest.java @@ -0,0 +1,86 @@ +package com.salessparrow.api.unit.lib.openAi; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyMap; +import static org.mockito.ArgumentMatchers.anyString; +import java.io.IOException; +import java.util.List; + +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; +import org.springframework.web.reactive.function.client.WebClientResponseException; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.salessparrow.api.exception.CustomException; +import com.salessparrow.api.helper.Common; +import com.salessparrow.api.helper.Scenario; +import com.salessparrow.api.lib.httpLib.HttpClient; +import com.salessparrow.api.lib.httpLib.HttpClient.HttpResponse; +import com.salessparrow.api.lib.openAi.OpenAiRequest; + +@SpringBootTest +@Import({ Common.class, OpenAiRequest.class }) +public class OpenAiRequestTest { + + @Autowired + private OpenAiRequest openAiRequest; + + @Autowired + private Common common; + + @MockBean + private HttpClient httpClientMock; + + @Test + void testOpenAiRequest() throws IOException { + List testDataItems = common + .loadScenariosData("classpath:data/unit/lib/openAi/openAiRequest.scenarios.json"); + MockedStatic httpClientMockedStatic = Mockito.mockStatic(HttpClient.class); + + for (Scenario testDataItem : testDataItems) { + ObjectMapper objectMapper = new ObjectMapper(); + + HttpResponse openAiMocksResponse = new HttpResponse(); + openAiMocksResponse + .setResponseBody(objectMapper.writeValueAsString(testDataItem.getMocks().get("makeRequest"))); + httpClientMockedStatic.when(() -> HttpClient.makePostRequest(anyString(), anyMap(), any(), anyInt())) + .thenReturn(openAiMocksResponse); + + HttpResponse actualResponse = openAiRequest.makeRequest(testDataItem.getInput().get("payload")); + String expectedOutput = objectMapper.writeValueAsString(testDataItem.getOutput()); + + assertEquals(actualResponse.getResponseBody(), expectedOutput); + } + httpClientMockedStatic.close(); + } + + @Test + void testOpenAiRequestError() throws IOException { + List testDataItems = common + .loadScenariosData("classpath:data/unit/lib/openAi/openAiRequestError.scenarios.json"); + MockedStatic httpClientMockedStatic = Mockito.mockStatic(HttpClient.class); + + for (Scenario testDataItem : testDataItems) { + int statusCode = (int) testDataItem.getInput().get("statusCode"); + httpClientMockedStatic.when(() -> HttpClient.makePostRequest(anyString(), anyMap(), any(), anyInt())) + .thenThrow(new WebClientResponseException("error", statusCode, "error", null, null, null)); + try { + openAiRequest.makeRequest(testDataItem.getInput().get("payload")); + } + catch (CustomException e) { + String expectedApiErrorIdentifier = (String) testDataItem.getOutput().get("apiErrorIdentifier"); + + assertEquals(e.getErrorObject().getApiErrorIdentifier(), expectedApiErrorIdentifier); + } + } + httpClientMockedStatic.close(); + } + +} diff --git a/src/test/java/com/salessparrow/api/unit/lib/salesforce/helper/MakeCompositeRequestTest.java b/src/test/java/com/salessparrow/api/unit/lib/salesforce/helper/MakeCompositeRequestTest.java new file mode 100644 index 00000000..25eb6487 --- /dev/null +++ b/src/test/java/com/salessparrow/api/unit/lib/salesforce/helper/MakeCompositeRequestTest.java @@ -0,0 +1,60 @@ +package com.salessparrow.api.unit.lib.salesforce.helper; + +import com.salessparrow.api.lib.globalConstants.SalesforceConstants; +import com.salessparrow.api.lib.httpLib.HttpClient; +import com.salessparrow.api.lib.salesforce.dto.CompositeRequestDto; +import com.salessparrow.api.lib.salesforce.helper.MakeCompositeRequest; +import com.salessparrow.api.lib.salesforce.helper.SalesforceRequest; +import com.salessparrow.api.lib.salesforce.helper.SalesforceRequestInterface; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.springframework.boot.test.context.SpringBootTest; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.*; + +@SpringBootTest +public class MakeCompositeRequestTest { + + @Mock + private SalesforceRequest salesforceRequest; + + @Mock + private SalesforceConstants salesforceConstants; + + @InjectMocks + private MakeCompositeRequest makeCompositeRequest; + + @BeforeEach + public void setUp() { + MockitoAnnotations.openMocks(this); + } + + @Test + public void testMakePostRequest() { + + List compositeRequests = new ArrayList<>(); + CompositeRequestDto requestDto = new CompositeRequestDto("endpoint", "method", null); + compositeRequests.add(requestDto); + + when(salesforceConstants.timeoutMillis()).thenReturn(5000); + + when(salesforceConstants.salesforceCompositeUrl(anyString())).thenReturn("composite_url"); + + when(salesforceRequest.makeRequest(anyString(), any(SalesforceRequestInterface.class))) + .thenReturn(new HttpClient.HttpResponse(200, "response", null, "application/json")); + + HttpClient.HttpResponse response = makeCompositeRequest.makePostRequest(compositeRequests, "user_id"); + + assertEquals(200, response.getStatusCode()); + assertEquals("response", response.getResponseBody()); + } + +} diff --git a/src/test/java/com/salessparrow/api/unit/lib/salesforce/helper/SalesforceOAuthTokenTest.java b/src/test/java/com/salessparrow/api/unit/lib/salesforce/helper/SalesforceOAuthTokenTest.java new file mode 100644 index 00000000..3bfdfd65 --- /dev/null +++ b/src/test/java/com/salessparrow/api/unit/lib/salesforce/helper/SalesforceOAuthTokenTest.java @@ -0,0 +1,88 @@ +package com.salessparrow.api.unit.lib.salesforce.helper; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.salessparrow.api.domain.SalesforceOauthToken; +import com.salessparrow.api.lib.AwsKms; +import com.salessparrow.api.lib.Util; +import com.salessparrow.api.lib.httpLib.HttpClient; +import com.salessparrow.api.lib.salesforce.helper.SalesforceOAuthToken; +import com.salessparrow.api.lib.salesforce.wrappers.SalesforceGetRefreshedAccessToken; +import com.salessparrow.api.repositories.SalesforceOauthTokenRepository; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +public class SalesforceOAuthTokenTest { + + @Mock + private AwsKms awsKms; + + @Mock + private SalesforceOauthTokenRepository salesforceOauthTokenRepository; + + @Mock + private SalesforceGetRefreshedAccessToken salesforceGetRefreshedAccessToken; + + @Mock + private Util util; + + @InjectMocks + private SalesforceOAuthToken salesforceOauthToken; + + @BeforeEach + public void setUp() { + MockitoAnnotations.openMocks(this); + } + + @Test + public void testFetchAccessToken() { + + SalesforceOauthToken sfOAuthToken = new SalesforceOauthToken(); + sfOAuthToken.setAccessToken("encrypted_access_token"); + + when(awsKms.decryptToken("encrypted_access_token")).thenReturn("decrypted_access_token"); + + String decryptedAccessToken = salesforceOauthToken.fetchAccessToken(sfOAuthToken); + + assertEquals("decrypted_access_token", decryptedAccessToken); + } + + @Test + public void testUpdateAndGetRefreshedAccessToken() throws Exception { + + SalesforceOauthToken sfOAuthToken = new SalesforceOauthToken(); + sfOAuthToken.setRefreshToken("encrypted_refresh_token"); + + String responseBody = "{\"access_token\":\"new_access_token\"}"; + ObjectMapper map = new ObjectMapper(); + JsonNode responseBodyNode = map.readTree(responseBody); + + when(awsKms.decryptToken("encrypted_refresh_token")).thenReturn("decrypted_refresh_token"); + + HttpClient.HttpResponse response = new HttpClient.HttpResponse(200, responseBody, null, "application/json"); + when(salesforceGetRefreshedAccessToken.getRefreshedAccessToken("decrypted_refresh_token")).thenReturn(response); + + when(util.getJsonNode(responseBody)).thenReturn(responseBodyNode); + + when(awsKms.encryptToken("new_access_token")).thenReturn("encrypted_access_token"); + + when(salesforceOauthTokenRepository.updateSalesforceOauthToken(any(SalesforceOauthToken.class))) + .thenAnswer(invocation -> invocation.getArgument(0)); // Return the argument + // back + + String decryptedAccessToken = salesforceOauthToken.updateAndGetRefreshedAccessToken(sfOAuthToken); + + assertEquals("new_access_token", decryptedAccessToken); + assertEquals(200, response.getStatusCode()); + assertTrue(responseBodyNode.has("access_token")); + } + +} diff --git a/src/test/java/com/salessparrow/api/unit/lib/salesforce/helper/SalesforceRequestTest.java b/src/test/java/com/salessparrow/api/unit/lib/salesforce/helper/SalesforceRequestTest.java new file mode 100644 index 00000000..e6cf281c --- /dev/null +++ b/src/test/java/com/salessparrow/api/unit/lib/salesforce/helper/SalesforceRequestTest.java @@ -0,0 +1,121 @@ +package com.salessparrow.api.unit.lib.salesforce.helper; + +import com.salessparrow.api.domain.SalesforceOauthToken; +import com.salessparrow.api.exception.CustomException; +import com.salessparrow.api.lib.salesforce.helper.SalesforceOAuthToken; +import com.salessparrow.api.lib.salesforce.helper.SalesforceRequest; +import com.salessparrow.api.lib.salesforce.helper.SalesforceRequestInterface; +import com.salessparrow.api.repositories.SalesforceOauthTokenRepository; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.web.reactive.function.client.WebClientResponseException; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +@SpringBootTest +public class SalesforceRequestTest { + + @Mock + private SalesforceOAuthToken salesforceOAuthToken; + + @Mock + private SalesforceOauthTokenRepository sfOauthTokenRepository; + + @InjectMocks + private SalesforceRequest salesforceRequest; + + @BeforeEach + public void setUp() { + MockitoAnnotations.openMocks(this); + } + + @Test + public void testMakeRequestSuccess() { + + SalesforceOauthToken sfOAuthToken = new SalesforceOauthToken(); + sfOAuthToken.setAccessToken("access_token"); + sfOAuthToken.setInstanceUrl("https://instance.url"); + + when(sfOauthTokenRepository.getSalesforceOauthTokenByExternalUserId("user123")).thenReturn(sfOAuthToken); + + // Mock SalesforceOAuthToken + when(salesforceOAuthToken.fetchAccessToken(any(SalesforceOauthToken.class))).thenReturn("access_token"); + + // Mock SalesforceRequestInterface + SalesforceRequestInterface requestInterface = (token, instanceUrl) -> "Response"; + + // Test the method + String response = salesforceRequest.makeRequest("user123", requestInterface); + + // Verify that the methods were called with the expected parameters + verify(sfOauthTokenRepository).getSalesforceOauthTokenByExternalUserId("user123"); + verify(salesforceOAuthToken).fetchAccessToken(sfOAuthToken); + + // Verify the response + assertEquals("Response", response); + } + + @Test + public void testMakeRequestWebClientResponseException() { + + SalesforceOauthToken sfOAuthToken = new SalesforceOauthToken(); + sfOAuthToken.setAccessToken("access_token"); + sfOAuthToken.setInstanceUrl("https://instance.url"); + + when(sfOauthTokenRepository.getSalesforceOauthTokenByExternalUserId("user123")).thenReturn(sfOAuthToken); + + // Mock SalesforceOAuthToken + when(salesforceOAuthToken.fetchAccessToken(any(SalesforceOauthToken.class))).thenReturn("access_token"); + + // Mock SalesforceRequestInterface + SalesforceRequestInterface requestInterface = (token, instanceUrl) -> { + throw new WebClientResponseException(401, "Unauthorized", null, null, null); + }; + + // Test the method and expect a CustomException + assertThrows(CustomException.class, () -> salesforceRequest.makeRequest("user123", requestInterface)); + + // Verify that the methods were called with the expected parameters + verify(sfOauthTokenRepository).getSalesforceOauthTokenByExternalUserId("user123"); + verify(salesforceOAuthToken).fetchAccessToken(sfOAuthToken); + + // Verify that the updateAndGetRefreshedAccessToken method was called once + verify(salesforceOAuthToken, times(1)).updateAndGetRefreshedAccessToken(sfOAuthToken); + } + + @Test + public void testMakeRequestInternalServerErrorException() { + + SalesforceOauthToken sfOAuthToken = new SalesforceOauthToken(); + sfOAuthToken.setAccessToken("access_token"); + sfOAuthToken.setInstanceUrl("https://instance.url"); + + when(sfOauthTokenRepository.getSalesforceOauthTokenByExternalUserId("user123")).thenReturn(sfOAuthToken); + + // Mock SalesforceOAuthToken + when(salesforceOAuthToken.fetchAccessToken(any(SalesforceOauthToken.class))).thenReturn("access_token"); + + // Mock SalesforceRequestInterface + SalesforceRequestInterface requestInterface = (token, instanceUrl) -> { + throw new WebClientResponseException(500, "Internal Server Error", null, null, null); + }; + + // Test the method and expect a CustomException + assertThrows(CustomException.class, () -> salesforceRequest.makeRequest("user123", requestInterface)); + + // Verify that the methods were called with the expected parameters + verify(sfOauthTokenRepository).getSalesforceOauthTokenByExternalUserId("user123"); + verify(salesforceOAuthToken).fetchAccessToken(sfOAuthToken); + + // Verify that the updateAndGetRefreshedAccessToken method was not called + verify(salesforceOAuthToken, never()).updateAndGetRefreshedAccessToken(sfOAuthToken); + } + +} diff --git a/src/test/java/com/salessparrow/api/unit/lib/salesforce/wrappers/SalesforceGetIdentityTest.java b/src/test/java/com/salessparrow/api/unit/lib/salesforce/wrappers/SalesforceGetIdentityTest.java new file mode 100644 index 00000000..28708919 --- /dev/null +++ b/src/test/java/com/salessparrow/api/unit/lib/salesforce/wrappers/SalesforceGetIdentityTest.java @@ -0,0 +1,99 @@ +package com.salessparrow.api.unit.lib.salesforce.wrappers; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyMap; +import static org.mockito.ArgumentMatchers.anyString; + +import java.util.HashMap; +import java.util.Map; + +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.Import; +import org.springframework.beans.factory.annotation.Autowired; +import com.salessparrow.api.exception.CustomException; +import com.salessparrow.api.lib.errorLib.ErrorObject; +import com.salessparrow.api.lib.globalConstants.SalesforceConstants; +import com.salessparrow.api.lib.httpLib.HttpClient; +import com.salessparrow.api.lib.httpLib.HttpClient.HttpResponse; +import com.salessparrow.api.lib.salesforce.wrappers.SalesforceGetIdentity; + +@SpringBootTest +@Import({ SalesforceGetIdentity.class, SalesforceConstants.class }) +public class SalesforceGetIdentityTest { + + @Autowired + private SalesforceGetIdentity salesforceGetIdentity; + + @Autowired + private SalesforceConstants salesforceConstants; + + @Test + public void testGetUserIdentitySuccess() throws Exception { + try (MockedStatic httpClientMockedStatic = Mockito.mockStatic(HttpClient.class)) { + + String instanceUrl = "https://example.com"; + String accessToken = "dummyAccessToken"; + + Map headers = new HashMap<>(); + headers.put("Authorization", "Bearer " + accessToken); + + String responseBody = "Mock Response Body"; + HttpResponse mockResponse = new HttpResponse(); + mockResponse.setResponseBody(responseBody); + + httpClientMockedStatic.when(() -> HttpClient.makeGetRequest(anyString(), anyMap(), anyInt())) + .thenReturn(mockResponse); + + HttpResponse actualResponse = salesforceGetIdentity.getUserIdentity(instanceUrl, accessToken); + + // Assertions + assertEquals(mockResponse.getResponseBody(), actualResponse.getResponseBody()); + + httpClientMockedStatic.verify( + () -> HttpClient.makeGetRequest(instanceUrl + salesforceConstants.identityUrl(), headers, 10000), + Mockito.times(1)); + } + } + + @Test + public void testGetUserIdentityException() throws Exception { + try (MockedStatic httpClientMockedStatic = Mockito.mockStatic(HttpClient.class)) { + + String instanceUrl = "https://example.com"; + String accessToken = "dummyAccessToken"; + + Map headers = new HashMap<>(); + headers.put("Authorization", "Bearer " + accessToken); + + String responseBody = "Mock Response Body"; + HttpResponse mockResponse = new HttpResponse(); + mockResponse.setResponseBody(responseBody); + + httpClientMockedStatic.when(() -> HttpClient.makeGetRequest(anyString(), anyMap(), anyInt())) + .thenThrow(new RuntimeException("Some error occurred")); + + CustomException exception = assertThrows(CustomException.class, () -> { + salesforceGetIdentity.getUserIdentity(instanceUrl, accessToken); + }); + + // Assertions + assertNotNull(exception); + ErrorObject errorObject = exception.getErrorObject(); + assertNotNull(errorObject); + assertEquals("l_s_w_sgi_gui_1", errorObject.getInternalErrorIdentifier()); + assertEquals("bad_request", errorObject.getApiErrorIdentifier()); + + httpClientMockedStatic.verify( + () -> HttpClient.makeGetRequest(instanceUrl + salesforceConstants.identityUrl(), headers, 10000), + Mockito.times(1)); + + } + } + +} diff --git a/src/test/java/com/salessparrow/api/unit/lib/salesforce/wrappers/SalesforceGetNoteContentTest.java b/src/test/java/com/salessparrow/api/unit/lib/salesforce/wrappers/SalesforceGetNoteContentTest.java new file mode 100644 index 00000000..dc667cc1 --- /dev/null +++ b/src/test/java/com/salessparrow/api/unit/lib/salesforce/wrappers/SalesforceGetNoteContentTest.java @@ -0,0 +1,80 @@ +package com.salessparrow.api.unit.lib.salesforce.wrappers; + +import com.salessparrow.api.lib.globalConstants.SalesforceConstants; +import com.salessparrow.api.lib.httpLib.HttpClient; +import com.salessparrow.api.lib.salesforce.helper.SalesforceRequest; +import com.salessparrow.api.lib.salesforce.helper.SalesforceRequestInterface; +import com.salessparrow.api.lib.salesforce.wrappers.SalesforceGetNoteContent; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.springframework.boot.test.context.SpringBootTest; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.*; + +@SpringBootTest +public class SalesforceGetNoteContentTest { + + @Mock + private SalesforceConstants salesforceConstants; + + @Mock + private SalesforceRequest salesforceRequest; + + @InjectMocks + private SalesforceGetNoteContent salesforceGetNoteContent; + + @BeforeEach + public void setUp() { + MockitoAnnotations.openMocks(this); + } + + @Test + public void testGetNoteContent() { + String noteId = "note123"; + String salesforceUserId = "user123"; + + // Mock SalesforceConstants + when(salesforceConstants.timeoutMillis()).thenReturn(5000); + + // Mock SalesforceRequest + when(salesforceRequest.makeRequest(eq(salesforceUserId), any(SalesforceRequestInterface.class))) + .thenReturn(new HttpClient.HttpResponse(200, "Note Content", null, "text/plain")); + + // Test the method + HttpClient.HttpResponse response = salesforceGetNoteContent.getNoteContent(noteId, salesforceUserId); + + // Verify that the methods were called with the expected parameters + verify(salesforceConstants).timeoutMillis(); + verify(salesforceRequest).makeRequest(eq(salesforceUserId), any(SalesforceRequestInterface.class)); + + // Verify the response + assertEquals(200, response.getStatusCode()); + assertEquals("Note Content", response.getResponseBody()); + assertEquals("text/plain", response.getContentType()); + } + + @Test + public void testGetNoteContentErrorScenario() { + String noteId = "note123"; + String salesforceUserId = "user123"; + + // Mock SalesforceConstants + when(salesforceConstants.timeoutMillis()).thenReturn(5000); + + // Mock SalesforceRequest to simulate an error + when(salesforceRequest.makeRequest(eq(salesforceUserId), any(SalesforceRequestInterface.class))) + .thenThrow(new RuntimeException("Simulated error")); + + // Test the method and verify that it handles the error correctly + assertThrows(RuntimeException.class, () -> salesforceGetNoteContent.getNoteContent(noteId, salesforceUserId)); + } + +} diff --git a/src/test/java/com/salessparrow/api/unit/lib/salesforce/wrappers/SalesforceGetRefreshedAccessTokenTest.java b/src/test/java/com/salessparrow/api/unit/lib/salesforce/wrappers/SalesforceGetRefreshedAccessTokenTest.java new file mode 100644 index 00000000..6269e024 --- /dev/null +++ b/src/test/java/com/salessparrow/api/unit/lib/salesforce/wrappers/SalesforceGetRefreshedAccessTokenTest.java @@ -0,0 +1,56 @@ +package com.salessparrow.api.unit.lib.salesforce.wrappers; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyMap; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.times; + +import java.util.HashMap; +import java.util.Map; + +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.Import; + +import com.salessparrow.api.lib.httpLib.HttpClient; +import com.salessparrow.api.lib.httpLib.HttpClient.HttpResponse; +import com.salessparrow.api.lib.salesforce.wrappers.SalesforceGetRefreshedAccessToken; + +@SpringBootTest +@Import({ SalesforceGetRefreshedAccessToken.class }) +public class SalesforceGetRefreshedAccessTokenTest { + + @Autowired + private SalesforceGetRefreshedAccessToken salesforceGetRefreshedAccessToken; + + @Test + public void testSalesforceGetRefreshedAccessToken() throws Exception { + try (MockedStatic httpClientMockedStatic = Mockito.mockStatic(HttpClient.class)) { + + String refreshToken = "dummyRefreshToken"; + + Map headers = new HashMap<>(); + headers.put("Authorization", "Bearer " + refreshToken); + + String responseBody = "Mock Response Body"; + HttpResponse mockResponse = new HttpResponse(); + mockResponse.setResponseBody(responseBody); + + httpClientMockedStatic.when(() -> HttpClient.makePostRequest(anyString(), anyMap(), anyString(), anyInt())) + .thenReturn(mockResponse); + + HttpResponse actualResponse = salesforceGetRefreshedAccessToken.getRefreshedAccessToken(refreshToken); + + // Assertions + assertEquals(mockResponse.getResponseBody(), actualResponse.getResponseBody()); + httpClientMockedStatic + .verify(() -> HttpClient.makePostRequest(anyString(), anyMap(), anyString(), anyInt()), times(1)); + + } + } + +} diff --git a/src/test/java/com/salessparrow/api/unit/lib/salesforce/wrappers/SalesforceTokensTest.java b/src/test/java/com/salessparrow/api/unit/lib/salesforce/wrappers/SalesforceTokensTest.java new file mode 100644 index 00000000..f845ec17 --- /dev/null +++ b/src/test/java/com/salessparrow/api/unit/lib/salesforce/wrappers/SalesforceTokensTest.java @@ -0,0 +1,134 @@ +package com.salessparrow.api.unit.lib.salesforce.wrappers; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyMap; +import static org.mockito.ArgumentMatchers.anyString; + +import java.util.HashMap; +import java.util.Map; + +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; + +import com.salessparrow.api.exception.CustomException; +import com.salessparrow.api.lib.errorLib.ParamErrorObject; +import com.salessparrow.api.lib.httpLib.HttpClient; +import com.salessparrow.api.lib.httpLib.HttpClient.HttpResponse; +import com.salessparrow.api.lib.salesforce.wrappers.SalesforceTokens; + +@SpringBootTest +@Import({ SalesforceTokens.class }) +public class SalesforceTokensTest { + + @MockBean + private HttpClient httpClientMock; + + @Autowired + private SalesforceTokens salesforceTokens; + + @Test + public void testRevokeTokensSuccess() throws Exception { + MockedStatic httpClientMockedStatic = Mockito.mockStatic(HttpClient.class); + + String instanceUrl = "https://example.com"; + String refreshToken = "your-refresh-token"; + + HttpResponse expectedResponse = new HttpResponse(); + expectedResponse.setResponseBody(""); + + httpClientMockedStatic.when(() -> HttpClient.makePostRequest(anyString(), anyMap(), any(), anyInt())) + .thenReturn(expectedResponse); + + HttpResponse response = salesforceTokens.revokeTokens(instanceUrl, refreshToken); + + assertEquals(expectedResponse, response); + + httpClientMockedStatic.close(); + } + + @Test + public void testRevokeTokensFailure() throws Exception { + MockedStatic httpClientMockedStatic = Mockito.mockStatic(HttpClient.class); + + String instanceUrl = "https://example.com"; + String refreshToken = "invalid-refresh-token"; + + httpClientMockedStatic.when(() -> HttpClient.makePostRequest(anyString(), anyMap(), any(), anyInt())) + .thenThrow(new RuntimeException("Invalid refresh token")); + + assertThrows(CustomException.class, () -> { + salesforceTokens.revokeTokens(instanceUrl, refreshToken); + }); + httpClientMockedStatic.close(); + } + + @Test + public void testGetTokensSuccess() throws Exception { + try (MockedStatic httpClientMockedStatic = Mockito.mockStatic(HttpClient.class)) { + + String code = "dummyCode"; + String redirectUri = "dummyRedirectUri"; + + Map headers = new HashMap<>(); + headers.put("Authorization", "Dummy Bearer Header"); + + String responseBody = "Mock Response Body"; + HttpResponse mockResponse = new HttpResponse(); + mockResponse.setResponseBody(responseBody); + + httpClientMockedStatic.when(() -> HttpClient.makePostRequest(anyString(), anyMap(), anyString(), anyInt())) + .thenReturn(mockResponse); + + HttpResponse actualResponse = salesforceTokens.getTokens(code, redirectUri); + + // Assertions + assertEquals(mockResponse.getResponseBody(), actualResponse.getResponseBody()); + httpClientMockedStatic.verify( + () -> HttpClient.makePostRequest(anyString(), anyMap(), anyString(), anyInt()), Mockito.times(1)); + } + + } + + @Test + public void testGetTokensException() throws Exception { + try (MockedStatic httpClientMockedStatic = Mockito.mockStatic(HttpClient.class)) { + + String code = "dummyCode"; + String redirectUri = "dummyRedirectUri"; + + Map headers = new HashMap<>(); + headers.put("Authorization", "Dummy Bearer Header"); + + String responseBody = "Mock Response Body"; + HttpResponse mockResponse = new HttpResponse(); + mockResponse.setResponseBody(responseBody); + + httpClientMockedStatic.when(() -> HttpClient.makePostRequest(anyString(), anyMap(), anyString(), anyInt())) + .thenThrow(new RuntimeException("Some error occurred")); + + CustomException exception = assertThrows(CustomException.class, () -> { + salesforceTokens.getTokens(code, redirectUri); + }); + + // Assertions + assertNotNull(exception); + ParamErrorObject paramErrorObject = exception.getParamErrorObject(); + assertNotNull(paramErrorObject); + assertEquals("l_s_w_sgt_gt_1", paramErrorObject.getInternalErrorIdentifier()); + + httpClientMockedStatic.verify( + () -> HttpClient.makePostRequest(anyString(), anyMap(), anyString(), anyInt()), Mockito.times(1)); + + } + } + +} diff --git a/src/test/java/com/salessparrow/api/unit/repositories/SalesforceOauthTokenRepositoryTest.java b/src/test/java/com/salessparrow/api/unit/repositories/SalesforceOauthTokenRepositoryTest.java new file mode 100644 index 00000000..ee5c7fa7 --- /dev/null +++ b/src/test/java/com/salessparrow/api/unit/repositories/SalesforceOauthTokenRepositoryTest.java @@ -0,0 +1,316 @@ +package com.salessparrow.api.unit.repositories; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.salessparrow.api.config.DynamoDBConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.Import; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.io.Resource; +import org.springframework.core.io.ResourceLoader; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.dynamobee.exception.DynamobeeException; +import com.salessparrow.api.domain.SalesforceOauthToken; +import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper; +import com.amazonaws.services.dynamodbv2.model.AmazonDynamoDBException; +import com.salessparrow.api.exception.CustomException; +import com.salessparrow.api.helper.Cleanup; +import com.salessparrow.api.helper.Common; +import com.salessparrow.api.helper.FixtureData; +import com.salessparrow.api.helper.LoadFixture; +import com.salessparrow.api.helper.Scenario; +import com.salessparrow.api.helper.Setup; +import com.salessparrow.api.lib.Util; +import com.salessparrow.api.repositories.SalesforceOauthTokenRepository; + +/** + * Unit test for SalesforceOauthTokenRepository. + * + */ +@SpringBootTest +@Import({ Setup.class, Cleanup.class, Common.class, LoadFixture.class }) +public class SalesforceOauthTokenRepositoryTest { + + @Autowired + private Setup setup; + + @Autowired + private Cleanup cleanup; + + @Autowired + private Common common; + + @Autowired + private LoadFixture loadFixture; + + @Autowired + private ResourceLoader resourceLoader; + + private DynamoDBMapper mockDynamoDBMapper; + + private SalesforceOauthTokenRepository realSalesforceOauthTokenRepository; + + private SalesforceOauthTokenRepository mockSalesforceOauthTokenRepository; + + @BeforeEach + public void setUp() throws DynamobeeException, IOException { + setup.perform(); + // Use your Spring bean here + DynamoDBMapper dynamoDBMapper = new DynamoDBConfiguration().dynamoDBMapper(); + this.realSalesforceOauthTokenRepository = new SalesforceOauthTokenRepository(dynamoDBMapper); + + this.mockDynamoDBMapper = mock(DynamoDBMapper.class); // Mocked instance + this.mockSalesforceOauthTokenRepository = new SalesforceOauthTokenRepository(mockDynamoDBMapper); + } + + @AfterEach + public void tearDown() { + cleanup.perform(); + } + + /** + * Test valid case for createSalesforceOauthToken method + */ + @Test + public void testValidCreateSalesforceOauthToken() { + // Valid Create Db Query + SalesforceOauthToken salesforceOauthTokenValid = new SalesforceOauthToken(); + salesforceOauthTokenValid.setExternalUserId("externalUserId-1"); + + SalesforceOauthToken salesforceOauthTokenResp = this.realSalesforceOauthTokenRepository + .createSalesforceOauthToken(salesforceOauthTokenValid); + assertEquals(salesforceOauthTokenValid.getExternalUserId(), salesforceOauthTokenResp.getExternalUserId()); + } + + /** + * Test invalid case for createSalesforceOauthToken method + */ + @Test + public void testInvalidCreateSalesforceOauthToken() { + // Invalid Create Db Query without partition key + SalesforceOauthToken salesforceOauthTokenInvalid = new SalesforceOauthToken(); + salesforceOauthTokenInvalid.setExternalUserId("externalUserId-2"); + + // Mock the behavior to throw an exception when save is called + doThrow(new AmazonDynamoDBException("mock db save error")).when(mockDynamoDBMapper) + .save(salesforceOauthTokenInvalid); + + // Test if CustomException is thrown with the expected error code + CustomException thrownException = assertThrows(CustomException.class, + () -> mockSalesforceOauthTokenRepository.createSalesforceOauthToken(salesforceOauthTokenInvalid)); + // Validate the error identifier to be a 500 error + assertEquals("something_went_wrong", thrownException.getErrorObject().getApiErrorIdentifier()); + } + + /** + * Test valid case for updateSalesforceOauthToken method + */ + @Test + public void testValidUpdateSalesforceOauthToken() { + // Valid Update Db Query + SalesforceOauthToken salesforceOauthTokenValid = new SalesforceOauthToken(); + salesforceOauthTokenValid.setExternalUserId("externalUserId-1"); + + SalesforceOauthToken salesforceOauthTokenResp = this.realSalesforceOauthTokenRepository + .updateSalesforceOauthToken(salesforceOauthTokenValid); + assertEquals(salesforceOauthTokenValid.getExternalUserId(), salesforceOauthTokenResp.getExternalUserId()); + } + + /** + * Test invalid case for updateSalesforceOauthToken method + */ + @Test + public void testInvalidUpdateSalesforceOauthToken() { + // Invalid Update Db Query without partition key + SalesforceOauthToken salesforceOauthTokenInvalid = new SalesforceOauthToken(); + salesforceOauthTokenInvalid.setExternalUserId("externalUserId-2"); + + // Mock the behavior to throw an exception when save is called + doThrow(new AmazonDynamoDBException("mock db save error")).when(mockDynamoDBMapper) + .save(salesforceOauthTokenInvalid); + + // Test if CustomException is thrown with the expected error code + CustomException thrownException = assertThrows(CustomException.class, + () -> mockSalesforceOauthTokenRepository.updateSalesforceOauthToken(salesforceOauthTokenInvalid)); + // Validate the error identifier to be a 500 error + assertEquals("something_went_wrong", thrownException.getErrorObject().getApiErrorIdentifier()); + } + + /** + * Test valid case for getSalesforceOauthTokenByExternalUserId method + */ + @Test + public void testValidGetSalesforceOauthTokenByExternalUserId() throws Exception { + + String currentFunctionName = new Object() { + }.getClass().getEnclosingMethod().getName(); + FixtureData fixtureData = common.loadFixture( + "classpath:fixtures/unit/repositories/salesforceOauthTokenRepository.fixture.json", + currentFunctionName); + loadFixture.perform(fixtureData); + + // Valid Get Db Query + SalesforceOauthToken salesforceOauthTokenResp = this.realSalesforceOauthTokenRepository + .getSalesforceOauthTokenByExternalUserId("0055i00000AUxQHAA1"); + assertEquals("0055i00000AUxQHAA1", salesforceOauthTokenResp.getExternalUserId()); + } + + /** + * Test invalid case for getSalesforceOauthTokenByExternalUserId method + */ + @Test + public void testInvalidGetSalesforceOauthTokenByExternalUserId() throws Exception { + + String testExternalUserId = "externalUserId-3"; + + // Mock the behavior to throw an exception when load is called + when(mockDynamoDBMapper.load(SalesforceOauthToken.class, testExternalUserId)) + .thenThrow(new AmazonDynamoDBException("mock db get error")); + + // Mock Invalid Get Db Query + // Test if CustomException is thrown with the expected error code + CustomException thrownException = assertThrows(CustomException.class, + () -> mockSalesforceOauthTokenRepository.getSalesforceOauthTokenByExternalUserId(testExternalUserId)); + // Validate the error identifier to be a 500 error + assertEquals("something_went_wrong", thrownException.getErrorObject().getApiErrorIdentifier()); + } + + /** + * Test valid case for insert method and verify the inserted data + * @throws Exception + */ + @Test + public void testInsert() throws Exception { + String currentFunctionName = new Object() { + }.getClass().getEnclosingMethod().getName(); + + List testDataItems = loadTestData(currentFunctionName); + + for (Scenario testDataItem : testDataItems) { + + ObjectMapper objectMapper = new ObjectMapper(); + SalesforceOauthToken salesforceOauthToken = objectMapper.readValue( + objectMapper.writeValueAsString(testDataItem.getInput()), + new TypeReference() { + }); + salesforceOauthToken.setCreatedAt(Util.getCurrentTimeInDateFormat()); + + SalesforceOauthToken insertedSalesforceOauthToken = this.realSalesforceOauthTokenRepository + .updateSalesforceOauthToken(salesforceOauthToken); + + assertNotNull(insertedSalesforceOauthToken); + assertNotNull(insertedSalesforceOauthToken.getExternalUserId()); + assertNotNull(insertedSalesforceOauthToken.getCreatedAt()); + assertNotNull(insertedSalesforceOauthToken.getUpdatedAt()); + } + } + + /** + * Test valid case for update method and verify the updated data + * @throws Exception + */ + @Test + public void testUpdate() throws Exception { + String currentFunctionName = new Object() { + }.getClass().getEnclosingMethod().getName(); + + FixtureData fixtureData = common.loadFixture( + "classpath:fixtures/unit/repositories/salesforceOauthTokenRepository.fixture.json", + currentFunctionName); + loadFixture.perform(fixtureData); + + List testDataItems = loadTestData(currentFunctionName); + + for (Scenario testDataItem : testDataItems) { + + ObjectMapper objectMapper = new ObjectMapper(); + SalesforceOauthToken salesforceOauthToken = objectMapper.readValue( + objectMapper.writeValueAsString(testDataItem.getInput()), + new TypeReference() { + }); + + SalesforceOauthToken existingSalesforceOauthToken = this.realSalesforceOauthTokenRepository + .getSalesforceOauthTokenByExternalUserId(salesforceOauthToken.getExternalUserId()); + + this.realSalesforceOauthTokenRepository.updateSalesforceOauthToken(salesforceOauthToken); + + SalesforceOauthToken updatedSalesforceOauthToken = this.realSalesforceOauthTokenRepository + .getSalesforceOauthTokenByExternalUserId(salesforceOauthToken.getExternalUserId()); + + assertNotNull(updatedSalesforceOauthToken); + assertNotNull(updatedSalesforceOauthToken.getUpdatedAt()); + assertEquals(updatedSalesforceOauthToken.getCreatedAt(), existingSalesforceOauthToken.getCreatedAt()); + assertEquals(updatedSalesforceOauthToken.getInstanceUrl(), existingSalesforceOauthToken.getInstanceUrl()); + assertEquals(updatedSalesforceOauthToken.getAccessToken(), salesforceOauthToken.getAccessToken()); + assertEquals(updatedSalesforceOauthToken.getRefreshToken(), salesforceOauthToken.getRefreshToken()); + } + } + + /** + * Test valid case for update with null attributes + * @throws Exception + */ + @Test + public void testUpdateWithNullAttributes() throws Exception { + String currentFunctionName = new Object() { + }.getClass().getEnclosingMethod().getName(); + + FixtureData fixtureData = common.loadFixture( + "classpath:fixtures/unit/repositories/salesforceOauthTokenRepository.fixture.json", + currentFunctionName); + loadFixture.perform(fixtureData); + + List testDataItems = loadTestData(currentFunctionName); + for (Scenario testDataItem : testDataItems) { + + ObjectMapper objectMapper = new ObjectMapper(); + SalesforceOauthToken salesforceOauthToken = objectMapper.readValue( + objectMapper.writeValueAsString(testDataItem.getInput()), + new TypeReference() { + }); + + SalesforceOauthToken existingSalesforceOauthToken = this.realSalesforceOauthTokenRepository + .getSalesforceOauthTokenByExternalUserId(salesforceOauthToken.getExternalUserId()); + + this.realSalesforceOauthTokenRepository.updateSalesforceOauthToken(salesforceOauthToken); + + SalesforceOauthToken updatedSalesforceOauthToken = this.realSalesforceOauthTokenRepository + .getSalesforceOauthTokenByExternalUserId(salesforceOauthToken.getExternalUserId()); + + assertEquals(updatedSalesforceOauthToken.getAccessToken(), salesforceOauthToken.getAccessToken()); + assertEquals(updatedSalesforceOauthToken.getIdToken(), existingSalesforceOauthToken.getIdToken()); + } + } + + /** + * Load test data scenarios from json file + * @throws Exception + */ + public List loadTestData(String key) throws IOException { + String scenariosPath = "classpath:data/unit/repositories/salesforceOauthTokenRepository.scenarios.json"; + Resource resource = resourceLoader.getResource(scenariosPath); + ObjectMapper objectMapper = new ObjectMapper(); + + Map> scenariosMap = new HashMap<>(); + scenariosMap = objectMapper.readValue(resource.getInputStream(), + new TypeReference>>() { + }); + return scenariosMap.get(key); + } + +} diff --git a/src/test/java/com/salessparrow/api/unit/repositories/SalesforceOrganizationRepositoryTest.java b/src/test/java/com/salessparrow/api/unit/repositories/SalesforceOrganizationRepositoryTest.java new file mode 100644 index 00000000..084c7935 --- /dev/null +++ b/src/test/java/com/salessparrow/api/unit/repositories/SalesforceOrganizationRepositoryTest.java @@ -0,0 +1,170 @@ +package com.salessparrow.api.unit.repositories; + +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.Import; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import java.io.IOException; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper; +import com.amazonaws.services.dynamodbv2.model.AmazonDynamoDBException; +import com.github.dynamobee.exception.DynamobeeException; +import com.salessparrow.api.config.DynamoDBConfiguration; +import com.salessparrow.api.domain.SalesforceOrganization; +import com.salessparrow.api.exception.CustomException; +import com.salessparrow.api.helper.Cleanup; +import com.salessparrow.api.helper.Common; +import com.salessparrow.api.helper.FixtureData; +import com.salessparrow.api.helper.LoadFixture; +import com.salessparrow.api.helper.Setup; +import com.salessparrow.api.repositories.SalesforceOrganizationRepository; + +@SpringBootTest +@Import({ Setup.class, Cleanup.class, Common.class, LoadFixture.class }) +public class SalesforceOrganizationRepositoryTest { + + @Autowired + private Setup setup; + + @Autowired + private Cleanup cleanup; + + @Autowired + private Common common; + + @Autowired + private LoadFixture loadFixture; + + private DynamoDBMapper mockDynamoDBMapper; + + private SalesforceOrganizationRepository realSalesforceOrganizationRepository; + + private SalesforceOrganizationRepository mockSalesforceOrganizationRepository; + + @BeforeEach + public void setUp() throws DynamobeeException, IOException { + setup.perform(); + // Use your Spring bean here + DynamoDBMapper dynamoDBMapper = new DynamoDBConfiguration().dynamoDBMapper(); + this.realSalesforceOrganizationRepository = new SalesforceOrganizationRepository(dynamoDBMapper); + + this.mockDynamoDBMapper = mock(DynamoDBMapper.class); // Mocked instance + this.mockSalesforceOrganizationRepository = new SalesforceOrganizationRepository(mockDynamoDBMapper); + } + + @AfterEach + public void tearDown() { + cleanup.perform(); + } + + /** + * Test Valid Save Db Query + */ + @Test + public void testValidCreateSalesforceOrganization() { + // Valid Create Db Query + SalesforceOrganization salesforceOrganizationValid = new SalesforceOrganization(); + salesforceOrganizationValid.setExternalOrganizationId("externalUserId-1"); + SalesforceOrganization salesforceOrganizationResp = this.realSalesforceOrganizationRepository + .createSalesforceOrganization(salesforceOrganizationValid); + assertEquals(salesforceOrganizationValid.getExternalOrganizationId(), + salesforceOrganizationResp.getExternalOrganizationId()); + } + + /** + * Test Invalid Create Db Query + */ + @Test + public void testInvalidCreateSalesforceOrganization() { + // Invalid Create Db Query without partition key + SalesforceOrganization salesforceOrganizationInvalid = new SalesforceOrganization(); + salesforceOrganizationInvalid.setExternalOrganizationId("externalUserId-2"); + + // Mock the behavior to throw an exception when save is called + doThrow(new AmazonDynamoDBException("mock db save error")).when(mockDynamoDBMapper) + .save(salesforceOrganizationInvalid); + + // Test if CustomException is thrown with the expected error code + CustomException thrownException = assertThrows(CustomException.class, + () -> mockSalesforceOrganizationRepository.createSalesforceOrganization(salesforceOrganizationInvalid)); + // Validate the error identifier to be a 500 error + assertEquals("something_went_wrong", thrownException.getErrorObject().getApiErrorIdentifier()); + } + + /** + * Test Valid Save Db Query + */ + @Test + public void testValidUpdateSalesforceOrganization() { + // Valid Save Db Query + SalesforceOrganization salesforceOrganizationValid = new SalesforceOrganization(); + salesforceOrganizationValid.setExternalOrganizationId("externalUserId-1"); + SalesforceOrganization salesforceOrganizationResp = this.realSalesforceOrganizationRepository + .updateSalesforceOrganization(salesforceOrganizationValid); + assertEquals(salesforceOrganizationValid.getExternalOrganizationId(), + salesforceOrganizationResp.getExternalOrganizationId()); + } + + /** + * Test Invalid Save Db Query + */ + @Test + public void testInvalidUpdateSalesforceOrganization() { + // Invalid Save Db Query without partition key + SalesforceOrganization salesforceOrganizationInvalid = new SalesforceOrganization(); + salesforceOrganizationInvalid.setExternalOrganizationId("externalUserId-2"); + + // Mock the behavior to throw an exception when save is called + doThrow(new AmazonDynamoDBException("mock db save error")).when(mockDynamoDBMapper) + .save(salesforceOrganizationInvalid); + + // Test if CustomException is thrown with the expected error code + CustomException thrownException = assertThrows(CustomException.class, + () -> mockSalesforceOrganizationRepository.updateSalesforceOrganization(salesforceOrganizationInvalid)); + // Validate the error identifier to be a 500 error + assertEquals("something_went_wrong", thrownException.getErrorObject().getApiErrorIdentifier()); + } + + /** + * Test Valid Get Db Query + */ + @Test + public void testValidGetSalesforceOrganizationByExternalOrganizationId() throws Exception { + String currentFunctionName = new Object() { + }.getClass().getEnclosingMethod().getName(); + FixtureData fixtureData = common.loadFixture( + "classpath:fixtures/unit/repositories/salesforceOrganizationRepository.json", currentFunctionName); + loadFixture.perform(fixtureData); + + // Valid Get Db Query + SalesforceOrganization salesforceOrganizationResp = this.realSalesforceOrganizationRepository + .getSalesforceOrganizationByExternalOrganizationId("000Org-id"); + assertEquals("000Org-id", salesforceOrganizationResp.getExternalOrganizationId()); + } + + /** + * Test Invalid Get Db Query + */ + @Test + public void testInvalidGetSalesforceOrganizationByExternalOrganizationId() throws Exception { + String testExternalOrganizationId = "externalUserId-2"; + + // Mock the behavior to throw an exception when load is called + when(mockDynamoDBMapper.load(SalesforceOrganization.class, testExternalOrganizationId)) + .thenThrow(new AmazonDynamoDBException("mock db get error")); + + // Mock Invalid Get Db Query + // Test if CustomException is thrown with the expected error code + CustomException thrownException = assertThrows(CustomException.class, () -> mockSalesforceOrganizationRepository + .getSalesforceOrganizationByExternalOrganizationId(testExternalOrganizationId)); + // Validate the error identifier to be a 500 error + assertEquals("something_went_wrong", thrownException.getErrorObject().getApiErrorIdentifier()); + } + +} diff --git a/src/test/java/com/salessparrow/api/unit/repositories/SalesforceUserRepositoryTest.java b/src/test/java/com/salessparrow/api/unit/repositories/SalesforceUserRepositoryTest.java new file mode 100644 index 00000000..85081722 --- /dev/null +++ b/src/test/java/com/salessparrow/api/unit/repositories/SalesforceUserRepositoryTest.java @@ -0,0 +1,174 @@ +package com.salessparrow.api.unit.repositories; + +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.Import; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.io.IOException; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper; +import com.amazonaws.services.dynamodbv2.model.AmazonDynamoDBException; +import com.github.dynamobee.exception.DynamobeeException; +import com.salessparrow.api.config.DynamoDBConfiguration; +import com.salessparrow.api.domain.SalesforceUser; +import com.salessparrow.api.exception.CustomException; +import com.salessparrow.api.helper.Cleanup; +import com.salessparrow.api.helper.Common; +import com.salessparrow.api.helper.FixtureData; +import com.salessparrow.api.helper.LoadFixture; +import com.salessparrow.api.helper.Setup; +import com.salessparrow.api.repositories.SalesforceUserRepository; + +@SpringBootTest +@Import({ Setup.class, Cleanup.class, Common.class, LoadFixture.class }) +public class SalesforceUserRepositoryTest { + + @Autowired + private Setup setup; + + @Autowired + private Cleanup cleanup; + + @Autowired + private Common common; + + @Autowired + private LoadFixture loadFixture; + + private DynamoDBMapper mockDynamoDBMapper; + + private SalesforceUserRepository realSalesforceUserRepository; + + private SalesforceUserRepository mockSalesforceUserRepository; + + @BeforeEach + public void setUp() throws DynamobeeException, IOException { + setup.perform(); + // Use your Spring bean here + DynamoDBMapper dynamoDBMapper = new DynamoDBConfiguration().dynamoDBMapper(); + this.realSalesforceUserRepository = new SalesforceUserRepository(dynamoDBMapper); + + this.mockDynamoDBMapper = mock(DynamoDBMapper.class); // Mocked instance + this.mockSalesforceUserRepository = new SalesforceUserRepository(mockDynamoDBMapper); + } + + @AfterEach + public void tearDown() { + cleanup.perform(); + } + + /** + * Test Valid Create Db Query + * + */ + @Test + public void testValidCreateSalesforceUser() { + // Valid Create Db Query + SalesforceUser salesforceUserValid = new SalesforceUser(); + salesforceUserValid.setExternalUserId("externalUserId-test1"); + SalesforceUser salesforceUserResp = this.realSalesforceUserRepository.createSalesforceUser(salesforceUserValid); + assertEquals(salesforceUserValid.getExternalUserId(), salesforceUserResp.getExternalUserId()); + } + + /** + * Test Invalid Create Db Query + * + */ + @Test + public void testInvalidCreateSalesforceUser() { + // Invalid Create Db Query without partition key + SalesforceUser salesforceUserInvalid = new SalesforceUser(); + salesforceUserInvalid.setExternalUserId("externalUserId-test2"); + + // Mock the behavior to throw an exception when save is called + doThrow(new AmazonDynamoDBException("mock db save error")).when(mockDynamoDBMapper).save(salesforceUserInvalid); + + // Test if CustomException is thrown with the expected error code + CustomException thrownException = assertThrows(CustomException.class, + () -> mockSalesforceUserRepository.createSalesforceUser(salesforceUserInvalid)); + // Validate the error identifier to be a 500 error + assertEquals("something_went_wrong", thrownException.getErrorObject().getApiErrorIdentifier()); + } + + /** + * Test Valid Update Db Query + * + */ + @Test + public void testValidUpdateSalesforceUser() { + // Valid Update Db Query + SalesforceUser salesforceUserValid = new SalesforceUser(); + salesforceUserValid.setExternalUserId("externalUserId-test1"); + SalesforceUser salesforceUserResp = this.realSalesforceUserRepository.updateSalesforceUser(salesforceUserValid); + assertEquals(salesforceUserValid.getExternalUserId(), salesforceUserResp.getExternalUserId()); + } + + /** + * Test Invalid Update Db Query + * + */ + @Test + public void testInvalidUpdateSalesforceUser() { + // Invalid Update Db Query without partition key + SalesforceUser salesforceUserInvalid = new SalesforceUser(); + salesforceUserInvalid.setExternalUserId("externalUserId-test2"); + + // Mock the behavior to throw an exception when save is called + doThrow(new AmazonDynamoDBException("mock db save error")).when(mockDynamoDBMapper).save(salesforceUserInvalid); + + // Test if CustomException is thrown with the expected error code + CustomException thrownException = assertThrows(CustomException.class, + () -> mockSalesforceUserRepository.updateSalesforceUser(salesforceUserInvalid)); + // Validate the error identifier to be a 500 error + assertEquals("something_went_wrong", thrownException.getErrorObject().getApiErrorIdentifier()); + } + + /** + * Test Valid Get Db Query + * @throws Exception + */ + @Test + public void testValidGetSalesforceUserByExternalUserId() throws Exception { + String currentFunctionName = new Object() { + }.getClass().getEnclosingMethod().getName(); + FixtureData fixtureData = common + .loadFixture("classpath:fixtures/unit/repositories/salesforceUserRepository.json", currentFunctionName); + loadFixture.perform(fixtureData); + + // Valid Get Db Query + SalesforceUser salesforceUserResp = this.realSalesforceUserRepository + .getSalesforceUserByExternalUserId("0055i00000AUxQHAA1"); + assertEquals("0055i00000AUxQHAA1", salesforceUserResp.getExternalUserId()); + } + + /** + * Test Invalid Get Db Query + * @throws Exception + */ + @Test + public void testInvalidGetSalesforceUserByExternalUserId() throws Exception { + String testExternalUserId = "externalUserId-test3"; + + // Mock the behavior to throw an exception when load is called + when(mockDynamoDBMapper.load(SalesforceUser.class, testExternalUserId)) + .thenThrow(new AmazonDynamoDBException("mock db get error")); + + // Mock Invalid Get Db Query + // Test if CustomException is thrown with the expected error code + CustomException thrownException = assertThrows(CustomException.class, + () -> mockSalesforceUserRepository.getSalesforceUserByExternalUserId(testExternalUserId)); + // Validate the error identifier to be a 500 error + assertEquals("something_went_wrong", thrownException.getErrorObject().getApiErrorIdentifier()); + } + +} diff --git a/src/test/java/com/salessparrow/api/unit/utility/MemcachedTest.java b/src/test/java/com/salessparrow/api/unit/utility/MemcachedTest.java index 5316e39b..78c7618e 100644 --- a/src/test/java/com/salessparrow/api/unit/utility/MemcachedTest.java +++ b/src/test/java/com/salessparrow/api/unit/utility/MemcachedTest.java @@ -4,7 +4,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; -import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.springframework.cache.Cache; @@ -15,75 +14,75 @@ class MemcachedTest { - @Mock - private MemcachedClient memcachedClient; + @Mock + private MemcachedClient memcachedClient; - private Cache memcachedCache; + private Cache memcachedCache; - @BeforeEach - void setUp() { - MockitoAnnotations.openMocks(this); - memcachedCache = new Memcached("testCache", 3600, memcachedClient); - } + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + memcachedCache = new Memcached("testCache", 3600, memcachedClient); + } - @Test - void testGetName() { - assertEquals("testCache", memcachedCache.getName()); - } + @Test + void testGetName() { + assertEquals("testCache", memcachedCache.getName()); + } - @Test - void testGetNativeCache() { - assertEquals(memcachedClient, memcachedCache.getNativeCache()); - } + @Test + void testGetNativeCache() { + assertEquals(memcachedClient, memcachedCache.getNativeCache()); + } - @Test - void testGetCacheHit() { - String key = "testKey"; - String value = "testValue"; - when(memcachedClient.get("testCache_" + key)).thenReturn(value); + @Test + void testGetCacheHit() { + String key = "testKey"; + String value = "testValue"; + when(memcachedClient.get("testCache_" + key)).thenReturn(value); - Cache.ValueWrapper result = memcachedCache.get(key); + Cache.ValueWrapper result = memcachedCache.get(key); - assertNotNull(result); - assertEquals(value, result.get()); - verify(memcachedClient, times(1)).get("testCache_" + key); - } + assertNotNull(result); + assertEquals(value, result.get()); + verify(memcachedClient, times(1)).get("testCache_" + key); + } - @Test - void testGetCacheMiss() { - String key = "nonExistentKey"; - when(memcachedClient.get("testCache_" + key)).thenReturn(null); + @Test + void testGetCacheMiss() { + String key = "nonExistentKey"; + when(memcachedClient.get("testCache_" + key)).thenReturn(null); - Cache.ValueWrapper result = memcachedCache.get(key); + Cache.ValueWrapper result = memcachedCache.get(key); - assertNull(result); - verify(memcachedClient, times(1)).get("testCache_" + key); - } + assertNull(result); + verify(memcachedClient, times(1)).get("testCache_" + key); + } - @Test - void testPut() { - String key = "testKey"; - String value = "testValue"; + @Test + void testPut() { + String key = "testKey"; + String value = "testValue"; - memcachedCache.put(key, value); + memcachedCache.put(key, value); - verify(memcachedClient, times(1)).set("testCache_" + key, 3600, value); - } + verify(memcachedClient, times(1)).set("testCache_" + key, 3600, value); + } - @Test - void testEvict() { - String key = "testKey"; + @Test + void testEvict() { + String key = "testKey"; - memcachedCache.evict(key); + memcachedCache.evict(key); - verify(memcachedClient, times(1)).delete("testCache_" + key); - } + verify(memcachedClient, times(1)).delete("testCache_" + key); + } - @Test - void testClear() { - memcachedCache.clear(); + @Test + void testClear() { + memcachedCache.clear(); - verify(memcachedClient, times(1)).flush(); - } -} + verify(memcachedClient, times(1)).flush(); + } +} diff --git a/src/test/resources/data/controllers/accountController/createNote.scenarios.json b/src/test/resources/data/controllers/accountController/createNote.scenarios.json deleted file mode 100644 index 76a7eb37..00000000 --- a/src/test/resources/data/controllers/accountController/createNote.scenarios.json +++ /dev/null @@ -1,203 +0,0 @@ -[ - { - "description": "Should successfully create the note", - "input": { - "body": { - "text": "Test note" - }, - "accountId": "0011e00000bWSxdAAG", - "cookie": "1:00112233445566AAA1:SALESFORCE:1899000000:IbFOqqub193hajGqMDwrF41iUiKl8KJKWpmPl7oqX4eesrydP9cSx483f86OFK+dtSNAukoy+Tr7pRYsD3yVNw==" - }, - "mocks": { - "makeCompositeRequest": { - "compositeResponse": [ - { - "body": { - "id": "0691e000001WaSzAAK", - "success": true, - "errors": [] - }, - "httpHeaders": { - "Location": "/services/data/v58.0/sobjects/ContentNote/0691e000001WaSzAAK" - }, - "httpStatusCode": 201, - "referenceId": "createNote" - }, - { - "body": { - "id": "06A1e000002iD9mEAE", - "success": true, - "errors": [] - }, - "httpHeaders": { - "Location": "/services/data/v58.0/sobjects/ContentDocumentLink/06A1e000002iD9mEAE" - }, - "httpStatusCode": 201, - "referenceId": "attachNote" - } - ] - } - }, - "output": { - "note_id": "0691e000001WaSzAAK" - } - }, - { - "description": "Should fail when the note text is empty", - "input": { - "body": { - "text": "" - }, - "accountId": "0011e00000bWSxdAAG", - "cookie": "1:00112233445566AAA1:SALESFORCE:1899000000:IbFOqqub193hajGqMDwrF41iUiKl8KJKWpmPl7oqX4eesrydP9cSx483f86OFK+dtSNAukoy+Tr7pRYsD3yVNw==" - }, - "mocks": {}, - "output": { - "http_code": 400, - "message": "At least one parameter is invalid or missing.", - "code": "INVALID_PARAMS", - "internal_error_identifier": "b_2", - "param_errors": [ - { - "parameter": "text", - "param_error_identifier": "missing_text", - "message": "text is required parameter. Please provide text." - } - ] - } - }, - { - "description": "Should fail when the note text is not provided", - "input": { - "body": {}, - "accountId": "0011e00000bWSxdAAG", - "cookie": "1:00112233445566AAA1:SALESFORCE:1899000000:IbFOqqub193hajGqMDwrF41iUiKl8KJKWpmPl7oqX4eesrydP9cSx483f86OFK+dtSNAukoy+Tr7pRYsD3yVNw==" - }, - "mocks": {}, - "output": { - "http_code": 400, - "message": "At least one parameter is invalid or missing.", - "code": "INVALID_PARAMS", - "internal_error_identifier": "b_2", - "param_errors": [ - { - "parameter": "text", - "param_error_identifier": "missing_text", - "message": "text is required parameter. Please provide text." - } - ] - } - }, - { - "description": "Should fail when the cookie is invalid", - "input": { - "body": {}, - "accountId": "0011e00000bWSxdAAG", - "cookie": "1:00112233445566AAA1:SALESFORCE:1899000000:invalid" - }, - "mocks": {}, - "output": { - "http_code": 401, - "message": "Unauthorized API request. Please check your API key.", - "code": "UNAUTHORIZED", - "internal_error_identifier": "l_ulca_vct_2", - "param_errors": [] - } - }, - { - "description": "Should fail when the account id invalid", - "input": { - "body": { - "text": "Test note" - }, - "accountId": "invalid", - "cookie": "1:00112233445566AAA1:SALESFORCE:1899000000:IbFOqqub193hajGqMDwrF41iUiKl8KJKWpmPl7oqX4eesrydP9cSx483f86OFK+dtSNAukoy+Tr7pRYsD3yVNw==" - }, - "mocks": { - "makeCompositeRequest": { - "compositeResponse": [ - { - "body": { - "id": "0691e000001WvohAAC", - "success": true, - "errors": [] - }, - "httpHeaders": { - "Location": "/services/data/v58.0/sobjects/ContentNote/0691e000001WvohAAC" - }, - "httpStatusCode": 201, - "referenceId": "CreateNote" - }, - { - "body": [ - { - "message": "Linked Entity ID: id value of incorrect type: 0011e00000bWSxdAAkk", - "errorCode": "MALFORMED_ID", - "fields": [ - "LinkedEntityId" - ] - } - ], - "httpHeaders": {}, - "httpStatusCode": 400, - "referenceId": "AttachNote" - } - ] - } - }, - "output": { - "http_code": 500, - "message": "Something went wrong.", - "code": "INTERNAL_SERVER_ERROR", - "internal_error_identifier": "l_s_fse_fscn_fcn_2", - "param_errors": [] - } - }, - { - "description": "Should successfully create the note when note length is greater than 50 chars", - "input": { - "body": { - "text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat." - }, - "accountId": "0011e00000bWSxdAAG", - "cookie": "1:00112233445566AAA1:SALESFORCE:1899000000:IbFOqqub193hajGqMDwrF41iUiKl8KJKWpmPl7oqX4eesrydP9cSx483f86OFK+dtSNAukoy+Tr7pRYsD3yVNw==" - }, - "mocks": { - "makeCompositeRequest": { - "compositeResponse": [ - { - "body": { - "id": "0691e000001WaSzAAK", - "success": true, - "errors": [] - }, - "httpHeaders": { - "Location": "/services/data/v58.0/sobjects/ContentNote/0691e000001WaSzAAK" - }, - "httpStatusCode": 500, - "referenceId": "createNote" - }, - { - "body": { - "id": "06A1e000002iD9mEAE", - "success": true, - "errors": [] - }, - "httpHeaders": { - "Location": "/services/data/v58.0/sobjects/ContentDocumentLink/06A1e000002iD9mEAE" - }, - "httpStatusCode": 201, - "referenceId": "attachNote" - } - ] - } - }, - "output": { - "http_code": 500, - "message": "Something went wrong.", - "code": "INTERNAL_SERVER_ERROR", - "internal_error_identifier": "l_s_fse_fscn_fcn_1", - "param_errors": [] - } - } -] \ No newline at end of file diff --git a/src/test/resources/data/controllers/accountController/getNoteDetails.scenarios.json b/src/test/resources/data/controllers/accountController/getNoteDetails.scenarios.json deleted file mode 100644 index 77f4f83c..00000000 --- a/src/test/resources/data/controllers/accountController/getNoteDetails.scenarios.json +++ /dev/null @@ -1,90 +0,0 @@ -[ - { - "description": "Should return the 5 latest Notes", - "input": { - "accountId":"0011e00000bWSxdAAG", - "noteId":"0691e000001WY58AAG", - "cookie": "1:00112233445566AAA1:SALESFORCE:1899000000:IbFOqqub193hajGqMDwrF41iUiKl8KJKWpmPl7oqX4eesrydP9cSx483f86OFK+dtSNAukoy+Tr7pRYsD3yVNw==" - }, - "mocks":{ - "makeCompositeRequest":{ - "compositeResponse": [ - { - "body": { - "totalSize": 1, - "done": true, - "records": [ - { - "attributes": { - "type": "ContentNote", - "url": "/services/data/v58.0/sobjects/ContentNote/0691e000001WY58AAG" - }, - "Id": "0691e000001WY58AAG", - "Title": "Test Note with composite", - "TextPreview": "Let's use plural routes and have accountId in URL as per the API Convention.\\n/v1/account/content-notes => /v1/salesforce/accounts/{accountId}/content-notes\\n/v1/composite is generic. It can be under a salesforce route.\\n/v1/composite => /v1/salesforce/co", - "CreatedBy": { - "attributes": { - "type": "User", - "url": "/services/data/v58.0/sobjects/User/0055i00000AUxQHAA1" - }, - "Name": "Ashfaq Bhojani" - }, - "LastModifiedDate": "2023-08-01T10:38:23.000+0000" - } - ] - }, - "httpHeaders": {}, - "httpStatusCode": 200, - "referenceId": "ContentDocument" - } - ] - }, - "makeWrapperRequest":"

Let's use plural routes and have accountId in URL as per the API Convention.\n/v1/account/content-notes => /v1/salesforce/accounts/{accountId}/content-notes\n/v1/composite is generic. It can be under a salesforce route.\n/v1/composite => /v1/salesforce/composite\nQuestion:TextPreview in content notes response is a truncated version of the full text in notes?She suspicion dejection saw instantly. Well deny may real one told yet saw hard dear. Bed chief house rapid right the. Set noisy one state tears which. No girl oh part must fact high my he. Simplicity in excellence melancholy as remarkably discovered. Own partiality motionless was old excellence she inquietude contrasted. Sister giving so wicket cousin of an he rather marked. Of on game part body rich. Adapted mr savings venture it or comfort affixed friends.Prepared is me marianne pleasure likewise debating. Wonder an unable except better stairs do ye admire. His and eat secure sex called esteem praise. So moreover as speedily differed branched ignorant. Tall are her knew poor now does then. Procured to contempt oh he raptures amounted occasion. One boy assure income spirit lovers set.Passage its ten led hearted removal cordial. Preference any astonished unreserved mrs. Prosperous understood middletons in conviction an uncommonly do. Supposing so be resolving breakfast am or perfectly. Is drew am hill from mr. Valley by oh twenty direct me so. Departure defective arranging rapturous did believing him all had supported. Family months lasted simple set nature vulgar him. Picture for attempt joy excited ten carried manners talking how. Suspicion neglected he resolving agreement perceived at an.Dashwood contempt on mr unlocked resolved provided of of. Stanhill wondered it it welcomed oh. Hundred no prudent he however smiling at an offence. If earnestly extremity he he propriety something admitting convinced ye. Pleasant in to although as if differed horrible. Mirth his quick its set front enjoy hoped had there. Who connection imprudence middletons too but increasing celebrated principles joy. Herself too improve gay winding ask expense are compact. New all paid few hard pure she.That know ask case sex ham dear her spot. Weddings followed the all marianne nor whatever settling. Perhaps six prudent several her had offence. Did had way law dinner square tastes. Recommend concealed yet her procuring see consulted depending. Adieu

" - }, - "output":{ - "note_detail": { - "id": "0691e000001WY58AAG", - "creator": "Ashfaq Bhojani", - "text": "\"

Let's use plural routes and have accountId in URL as per the API Convention.\\n/v1/account/content-notes => /v1/salesforce/accounts/{accountId}/content-notes\\n/v1/composite is generic. It can be under a salesforce route.\\n/v1/composite => /v1/salesforce/composite\\nQuestion:TextPreview in content notes response is a truncated version of the full text in notes?She suspicion dejection saw instantly. Well deny may real one told yet saw hard dear. Bed chief house rapid right the. Set noisy one state tears which. No girl oh part must fact high my he. Simplicity in excellence melancholy as remarkably discovered. Own partiality motionless was old excellence she inquietude contrasted. Sister giving so wicket cousin of an he rather marked. Of on game part body rich. Adapted mr savings venture it or comfort affixed friends.Prepared is me marianne pleasure likewise debating. Wonder an unable except better stairs do ye admire. His and eat secure sex called esteem praise. So moreover as speedily differed branched ignorant. Tall are her knew poor now does then. Procured to contempt oh he raptures amounted occasion. One boy assure income spirit lovers set.Passage its ten led hearted removal cordial. Preference any astonished unreserved mrs. Prosperous understood middletons in conviction an uncommonly do. Supposing so be resolving breakfast am or perfectly. Is drew am hill from mr. Valley by oh twenty direct me so. Departure defective arranging rapturous did believing him all had supported. Family months lasted simple set nature vulgar him. Picture for attempt joy excited ten carried manners talking how. Suspicion neglected he resolving agreement perceived at an.Dashwood contempt on mr unlocked resolved provided of of. Stanhill wondered it it welcomed oh. Hundred no prudent he however smiling at an offence. If earnestly extremity he he propriety something admitting convinced ye. Pleasant in to although as if differed horrible. Mirth his quick its set front enjoy hoped had there. Who connection imprudence middletons too but increasing celebrated principles joy. Herself too improve gay winding ask expense are compact. New all paid few hard pure she.That know ask case sex ham dear her spot. Weddings followed the all marianne nor whatever settling. Perhaps six prudent several her had offence. Did had way law dinner square tastes. Recommend concealed yet her procuring see consulted depending. Adieu

\"", - "last_modified_time": "2023-08-01T10:38:23.000+00:00" - } - } - }, - { - "description": "should return error response when note id is invalid", - "input": { - "account_id": "0011e00000bWSxdAAG", - "note_id": "0011e00000bWSxdAAG", - "cookie": "1:00112233445566AAA1:SALESFORCE:1899000000:IbFOqqub193hajGqMDwrF41iUiKl8KJKWpmPl7oqX4eesrydP9cSx483f86OFK+dtSNAukoy+Tr7pRYsD3yVNw==" - }, - "mocks":{ - "makeCompositeRequest":{ - "compositeResponse": [ - { - "body": { - "totalSize": 0, - "done": true, - "records": [] - }, - "httpHeaders": {}, - "httpStatusCode": 200, - "referenceId": "Note" - } - ] - }, - "makeWrapperRequest":[ - { - "errorCode": "NOT_FOUND", - "message": "The requested resource does not exist" - } - ] - }, - "output": { - "http_code": 500, - "message": "Something went wrong.", - "code": "INTERNAL_SERVER_ERROR", - "internal_error_identifier": "l_c_gnd_gsnd_2", - "param_errors": [] - } - } -] \ No newline at end of file diff --git a/src/test/resources/data/functional/controllers/accountController/createNote.scenarios.json b/src/test/resources/data/functional/controllers/accountController/createNote.scenarios.json new file mode 100644 index 00000000..5ee878d4 --- /dev/null +++ b/src/test/resources/data/functional/controllers/accountController/createNote.scenarios.json @@ -0,0 +1,181 @@ +[ + { + "description": "Should successfully create the note", + "input": { + "body": { + "text": "Test note" + }, + "accountId": "0011e00000bWSxdAAG" + }, + "mocks": { + "makeCompositeRequest": { + "compositeResponse": [ + { + "body": { + "id": "0691e000001WaSzAAK", + "success": true, + "errors": [] + }, + "httpHeaders": { + "Location": "/services/data/v58.0/sobjects/ContentNote/0691e000001WaSzAAK" + }, + "httpStatusCode": 201, + "referenceId": "createNote" + }, + { + "body": { + "id": "06A1e000002iD9mEAE", + "success": true, + "errors": [] + }, + "httpHeaders": { + "Location": "/services/data/v58.0/sobjects/ContentDocumentLink/06A1e000002iD9mEAE" + }, + "httpStatusCode": 201, + "referenceId": "attachNote" + } + ] + } + }, + "output": { + "note_id": "0691e000001WaSzAAK" + } + }, + { + "description": "Should fail when the note text is empty", + "input": { + "body": { + "text": "" + }, + "accountId": "0011e00000bWSxdAAG" + }, + "mocks": {}, + "output": { + "http_code": 400, + "code": "INVALID_PARAMS", + "param_errors": [ + "missing_text" + ] + } + }, + { + "description": "Should fail when the note text is not provided", + "input": { + "body": {}, + "accountId": "0011e00000bWSxdAAG" + }, + "mocks": {}, + "output": { + "http_code": 400, + "code": "INVALID_PARAMS", + "param_errors": [ + "missing_text" + ] + } + }, + { + "description": "Should fail when the account id invalid", + "input": { + "body": { + "text": "Test note" + }, + "accountId": "invalid" + }, + "mocks": { + "makeCompositeRequest": { + "compositeResponse": [ + { + "body": { + "id": "0691e000001WvohAAC", + "success": true, + "errors": [] + }, + "httpHeaders": { + "Location": "/services/data/v58.0/sobjects/ContentNote/0691e000001WvohAAC" + }, + "httpStatusCode": 201, + "referenceId": "CreateNote" + }, + { + "body": [ + { + "message": "Linked Entity ID: id value of incorrect type: 0011e00000bWSxdAAkk", + "errorCode": "MALFORMED_ID", + "fields": [ + "LinkedEntityId" + ] + } + ], + "httpHeaders": {}, + "httpStatusCode": 400, + "referenceId": "AttachNote" + } + ] + } + }, + "output": { + "http_code": 500, + "code": "INTERNAL_SERVER_ERROR" + } + }, + { + "description": "Should successfully create the note when note length is greater than 50 chars", + "input": { + "body": { + "text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat." + }, + "accountId": "0011e00000bWSxdAAG" + }, + "mocks": { + "makeCompositeRequest": { + "compositeResponse": [ + { + "body": { + "id": "0691e000001WaSzAAK", + "success": true, + "errors": [] + }, + "httpHeaders": { + "Location": "/services/data/v58.0/sobjects/ContentNote/0691e000001WaSzAAK" + }, + "httpStatusCode": 500, + "referenceId": "createNote" + }, + { + "body": { + "id": "06A1e000002iD9mEAE", + "success": true, + "errors": [] + }, + "httpHeaders": { + "Location": "/services/data/v58.0/sobjects/ContentDocumentLink/06A1e000002iD9mEAE" + }, + "httpStatusCode": 201, + "referenceId": "attachNote" + } + ] + } + }, + "output": { + "http_code": 500, + "code": "INTERNAL_SERVER_ERROR" + } + }, + { + "description": "Should fail when the note text is too long", + "input": { + "body": { + "text": "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum. Nam quam nunc, blandit vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec odio et ante tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. Nullam quis ante. Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed consequat, leo eget bibendum sodales, augue velit cursus nunc, quis gravida magna mi a libero. Fusce vulputate eleifend sapien. Vestibulum purus quam, scelerisque ut, mollis sed, nonummy id, metus. Nullam accumsan lorem in dui. Cras ultricies mi eu turpis hendrerit fringilla. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; In ac dui quis mi consectetuer lacinia. Nam pretium turpis et arcu. Duis arcu tortor, suscipit eget, imperdiet nec, imperdiet iaculis, ipsum. Sed aliquam ultrices mauris. Integer ante arcu, accumsan a, consectetuer eget, posuere ut, mauris. Praesent adipiscing. Phasellus ullamcorper ipsum rutrum nunc. Nunc nonummy metus. Vestibulum volutpat pretium libero. Cras id dui. Aenean ut eros et nisl sagittis vestibulum. Nullam nulla eros, ultricies sit amet, nonummy id, imperdiet feugiat, pede. Sed lectus. Donec mollis hendrerit risus. Phasellus nec sem in justo pellentesque facilisis. Etiam imperdiet imperdiet orci. Nunc nec neque. Phasellus leo dolor, tempus non, auctor et, hendrerit quis, nisi. Curabitur ligula sapien, tincidunt non, euismod vitae, posuere imperdiet, leo. Maecenas malesuada. Praesent congue erat at massa. Sed cursus turpis vitae tortor. Donec posuere vulputate arcu. Phasellus accumsan cursus velit. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed aliquam, nisi quis porttitor congue, elit erat euismod orci, ac placerat dolor lectus quis orci. Phasellus consectetuer vestibulum elit. Aenean tellus metus, bibendum sed, posuere ac, mattis non, nunc. Vestibulum fringilla pede sit amet augue. In turpis. Pellentesque posuere. Praesent turpis. Aenean posuere, tortor sed cursus feugiat, nunc augue blandit nunc, eu sollicitudin urna dolor sagittis lacus. Donec elit libero, sodales nec, volutpat a, suscipit non, turpis. Nullam sagittis. Suspendisse pulvinar, augue ac venenatis condimentum, sem libero volutpat nibh, nec pellentesque velit pede quis nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Fusce id purus. Ut varius tincidunt libero. Phasellus dolor. Maecenas vestibulum mollis diam. Pellentesque ut neque. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. In dui magna, posuere eget, vestibulum et, tempor auctor, justo. In ac felis quis tortor malesuada pretium. Pellentesque auctor neque nec urna. Proin sapien ipsum, porta a, auctor quis, euismod ut, mi. Aenean viverra rhoncus pede. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Ut non enim eleifend felis pretium feugiat. Vivamus quis mi. Phasellus a est. Phasellus magna. In hac habitasse platea dictumst. Curabitur at lacus ac velit ornare lobortis. Curabitur a felis in nunc fringilla tristique. Morbi mattis ullamcorper velit. Phasellus gravida semper nisi. Nullam vel sem. Pellentesque libero tortor, tincidunt et, tincidunt eget, semper nec, quam. Sed hendrerit. Morbi ac felis. Nunc egestas, augue at pellentesque laoreet, felis eros vehicula leo, at malesuada velit leo quis pede. Donec interdum, metus et hendrerit aliquet, dolor diam sagittis ligula, eget egestas libero turpis vel mi. Nunc nulla. Fusce risus nisl, viverra et, tempor et, pretium in, sapien. Donec venenatis vulputate lorem. Morbi nec metus. Phasellus blandit leo ut odio. Maecenas ullamcorper, dui et placerat feugiat, eros pede varius nisi, condimentum viverra felis nunc et lorem. Sed magna purus, fermentum eu, tincidunt eu, varius ut, felis. In auctor lobortis lacus. Quisque libero metus, condimentum nec, tempor a, commodo mollis, magna. Vestibulum ullamcorper mauris at ligula. Fusce fermentum. Nullam cursus lacinia erat. Praesent blandit laoreet nibh. Fusce convallis metus id felis luctus adipiscing. Pellentesque egestas, neque sit amet convallis pulvinar, justo nulla eleifend augue, ac auctor orci leo non est. Quisque id mi. Ut tincidunt tincidunt erat. Etiam feugiat lorem non metus. Vestibulum dapibus nunc ac augue. Curabitur vestibulum aliquam leo. Praesent egestas neque eu enim. In hac habitasse platea dictumst. Fusce a quam. Etiam ut purus mattis mauris sodales aliquam. Curabitur nisi. Quisque malesuada placerat nisl. Nam ipsum risus, rutrum vitae, vestibulum eu, molestie vel, lacus. Sed augue ipsum, egestas nec, vestibulum et, malesuada adipiscing, dui. Vestibulum facilisis, purus nec pulvinar iaculis, ligula mi congue nunc, vitae euismod ligula urna in dolor. Mauris sollicitudin fermentum libero. Praesent nonummy mi in odio. Nunc interdum lacus sit amet orci. Vestibulum rutrum, mi nec elementum vehicula, eros quam gravida nisl, id fringilla neque ante vel mi. Morbi mollis tellus ac sapien. Phasellus volutpat, metus eget egestas mollis, lacus lacus blandit dui, id egestas quam mauris ut lacus. Fusce vel dui. Sed in libero ut nibh placerat accumsan. Proin faucibus arcu quis ante. In consectetuer turpis ut velit. Nulla sit amet est. Praesent metus tellus, elementum eu, semper a, adipiscing nec, purus. Cras risus ipsum, faucibus ut, ullamcorper id, varius ac, leo. Suspendisse feugiat. Suspendisse enim turpis, dictum sed, iaculis a, condimentum nec, nisi. Praesent nec nisl a purus blandit viverra. Praesent ac massa at ligula laoreet iaculis. Nulla neque dolor, sagittis eget, iaculis quis, molestie non, velit. Mauris turpis nunc, blandit et, volutpat molestie, porta ut, ligula. Fusce pharetra convallis urna. Quisque ut nisi. Donec mi odio, faucibus at, scelerisque quis, convallis in, nisi. Suspendisse non nisl sit amet velit hendrerit rutrum. Ut leo. Ut a nisl id ante tempus hendrerit. Proin pretium, leo ac pellentesque mollis, felis nunc ultrices eros, sed gravida augue augue mollis justo. Suspendisse eu ligula. Nulla facilisi. Donec id justo. Praesent porttitor, nulla vitae posuere iaculis, arcu nisl dignissim dolor, a pretium mi sem ut ipsum. Curabitur suscipit suscipit tellus. Praesent vestibulum dapibus nibh. Etiam iaculis nunc ac metus. Ut id nisl quis enim dignissim sagittis. Etiam sollicitudin, ipsum eu pulvinar rutrum, tellus ipsum laoreet sapien, quis venenatis ante odio sit amet eros. Proin magna. Duis vel nibh at velit scelerisque suscipit. Curabitur turpis. Vestibulum suscipit nulla quis orci. Fusce ac felis sit amet ligula pharetra condimentum. Maecenas egestas arcu quis ligula mattis placerat. Duis lobortis massa imperdiet quam. Suspendisse potenti. Pellentesque commodo eros a enim. Vestibulum turpis sem, aliquet eget, lobortis pellentesque, rutrum eu, nisl. Sed libero. Aliquam erat volutpat. Etiam vitae tortor. Morbi vestibulum volutpat enim. Aliquam eu nunc. Nunc sed turpis. Sed mollis, eros et ultrices tempus, mauris ipsum aliquam libero, non adipiscing dolor urna a orci. Nulla porta dolor. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Pellentesque dapibus hendrerit tortor. Praesent egestas tristique nibh. Sed a libero. Cras varius. Donec vitae orci sed dolor rutrum auctor. Fusce egestas elit eget lorem. Suspendisse nisl elit, rhoncus eget, elementum ac, condimentum eget, diam. Nam at tortor in tellus interdum sagittis. Aliquam lobortis. Donec orci lectus, aliquam ut, faucibus non, euismod id, nulla. Curabitur blandit mollis lacus. Nam adipiscing. Vestibulum eu odio. Vivamus laoreet. Nullam tincidunt adipiscing enim. Phasellus tempus. Proin viverra, ligula sit amet ultrices semper, ligula arcu tristique sapien, a accumsan nisi mauris ac eros. Fusce neque. Suspendisse faucibus, nunc et pellentesque egestas, lacus ante convallis tellus, vitae iaculis lacus elit id tortor. Vivamus aliquet elit ac nisl. Fusce fermentum odio nec arcu. Vivamus euismod mauris. In ut quam vitae odio lacinia tincidunt. Praesent ut ligula non mi varius sagittis. Cras sagittis. Praesent ac sem eget est egestas volutpat. Vivamus consectetuer hendrerit lacus. Cras non dolor. Vivamus in erat ut urna cursus vestibulum. Fusce commodo aliquam arcu. Nam commodo suscipit quam. Quisque id odio. Praesent venenatis metus at tortor pulvinar varius. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum. Nam quam nunc, blandit vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec odio et ante tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. Nullam quis ante. Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed consequat, leo eget bibendum sodales, augue velit cursus nunc, quis gravida magna mi a libero. Fusce vulputate eleifend sapien. Vestibulum purus quam, scelerisque ut, mollis sed, nonummy id, metus. Nullam accumsan lorem in dui. Cras ultricies mi eu turpis hendrerit fringilla. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; In ac dui quis mi consectetuer lacinia. Nam pretium turpis et arcu. Duis arcu tortor, suscipit eget, imperdiet nec, imperdiet iaculis, ipsum. Sed aliquam ultrices mauris. Integer ante arcu, accumsan a, consectetuer eget, posuere ut, mauris. Praesent adipiscing. Phasellus ullamcorper ipsum rutrum nunc. Nunc nonummy metus. Vestibulum volutpat pretium libero. Cras id dui. Aenean ut eros et nisl sagittis vestibulum. Nullam nulla eros, ultricies sit amet, nonummy id, imperdiet feugiat, pede. Sed lectus. Donec mollis hendrerit risus. Phasellus nec sem in justo pellentesque facilisis. Etiam imperdiet imperdiet orci. Nunc nec neque. Phasellus leo dolor, tempus non, auctor et, hendrerit quis, nisi. Curabitur ligula sapien, tincidunt non, euismod vitae, posuere imperdiet, leo. Maecenas malesuada. Praesent congue erat at massa. Sed cursus turpis vitae tortor. Donec posuere vulputate arcu. Phasellus accumsan cursus velit. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed aliquam, nisi quis porttitor" + }, + "accountId": "0011e00000bWSxdAAG" + }, + "mocks": {}, + "output": { + "http_code": 400, + "code": "INVALID_PARAMS", + "param_errors": [ + "text_too_long" + ] + } + } +] \ No newline at end of file diff --git a/src/test/resources/data/controllers/accountController/getAccountList.scenarios.json b/src/test/resources/data/functional/controllers/accountController/getAccountList.scenarios.json similarity index 84% rename from src/test/resources/data/controllers/accountController/getAccountList.scenarios.json rename to src/test/resources/data/functional/controllers/accountController/getAccountList.scenarios.json index 8ee16ae0..faa29b64 100644 --- a/src/test/resources/data/controllers/accountController/getAccountList.scenarios.json +++ b/src/test/resources/data/functional/controllers/accountController/getAccountList.scenarios.json @@ -39,17 +39,21 @@ }, "output": { "account_ids": [ - "0011e00000dWhY5AAK", - "0011e00000bQwsKAAS" + "0011e00000dWhY5AAK", + "0011e00000bQwsKAAS" ], "account_map_by_id": { - "0011e00000dWhY5AAK": { - "id": "0011e00000dWhY5AAK", - "name": "Test 1" - }, "0011e00000bQwsKAAS": { "id": "0011e00000bQwsKAAS", - "name": "Test 2" + "name": "Test 2", + "additional_fields": {}, + "account_contact_associations_id": "0011e00000bQwsKAAS" + }, + "0011e00000dWhY5AAK": { + "id": "0011e00000dWhY5AAK", + "name": "Test 1", + "additional_fields": {}, + "account_contact_associations_id": "0011e00000dWhY5AAK" } } } @@ -94,17 +98,21 @@ }, "output": { "account_ids": [ - "0011e00000dWhY5AAK", - "0011e00000bQwsKAAS" + "0011e00000dWhY5AAK", + "0011e00000bQwsKAAS" ], "account_map_by_id": { - "0011e00000dWhY5AAK": { - "id": "0011e00000dWhY5AAK", - "name": "Test 1" - }, "0011e00000bQwsKAAS": { "id": "0011e00000bQwsKAAS", - "name": "Test 2" + "name": "Test 2", + "additional_fields": {}, + "account_contact_associations_id": "0011e00000bQwsKAAS" + }, + "0011e00000dWhY5AAK": { + "id": "0011e00000dWhY5AAK", + "name": "Test 1", + "additional_fields": {}, + "account_contact_associations_id": "0011e00000dWhY5AAK" } } } diff --git a/src/test/resources/data/functional/controllers/accountController/getAccountsFeed.scenarios.json b/src/test/resources/data/functional/controllers/accountController/getAccountsFeed.scenarios.json new file mode 100644 index 00000000..b73fbe0e --- /dev/null +++ b/src/test/resources/data/functional/controllers/accountController/getAccountsFeed.scenarios.json @@ -0,0 +1,171 @@ +[ + { + "description": "Should return the accounts feed data sorted by lastmodifieddate", + "input": { + "pagination_identifier": "eyJwYWdlTnVtYmVyIjozfQ==" + }, + "mocks": { + "makeCompositeRequest": { + "compositeResponse": [ + { + "body": { + "totalSize": 2, + "done": true, + "records": [ + { + "attributes": { + "type": "Account", + "url": "/services/data/v58.0/sobjects/Account/0011e00000bAd2wAAC" + }, + "Id": "0011e00000dWhY5AAK", + "Name": "thenaIntegration", + "Website": "www.xyz.com", + "Contacts": null + }, + { + "attributes": { + "type": "Account", + "url": "/services/data/v58.0/sobjects/Account/0011e00000asaDXAAY" + }, + "Id": "0011e00000bQwsKAAS", + "Name": "Woomie", + "Website": "www.abc.com", + "Contacts": { + "totalSize": 2, + "done": true, + "records": [ + { + "attributes": { + "type": "Contact", + "url": "/services/data/v58.0/sobjects/Contact/0031e00000MFIHdAAP" + }, + "Id": "0031e00000MFIHdAAP", + "Name": "Sample user1", + "Title": "Sample title1" + }, + { + "attributes": { + "type": "Contact", + "url": "/services/data/v58.0/sobjects/Contact/0031e00000MFJcBAAX" + }, + "Id": "0031e00000MFJcBAAX", + "Name": "Sample user2", + "Title": "Sample title2" + } + ] + } + } + ] + }, + "httpHeaders": {}, + "httpStatusCode": 200, + "referenceId": "getAccounts" + } + ] + } + }, + "output": { + "account_ids": [ + "0011e00000dWhY5AAK", + "0011e00000bQwsKAAS" + ], + "account_map_by_id": { + "0011e00000bQwsKAAS": { + "id": "0011e00000bQwsKAAS", + "name": "Woomie", + "additional_fields": { + "website": "www.abc.com" + }, + "account_contact_associations_id": "0011e00000bQwsKAAS" + }, + "0011e00000dWhY5AAK": { + "id": "0011e00000dWhY5AAK", + "name": "thenaIntegration", + "additional_fields": { + "website": "www.xyz.com" + }, + "account_contact_associations_id": "0011e00000dWhY5AAK" + } + }, + "contact_map_by_id": { + "0031e00000MFJcBAAX": { + "id": "0031e00000MFJcBAAX", + "name": "Sample user2", + "additional_fields": { + "title": "Sample title2" + } + }, + "0031e00000MFIHdAAP": { + "id": "0031e00000MFIHdAAP", + "name": "Sample user1", + "additional_fields": { + "title": "Sample title1" + } + } + }, + "account_contact_associations_map_by_id": { + "0011e00000bQwsKAAS": { + "contact_ids": [ + "0031e00000MFIHdAAP", + "0031e00000MFJcBAAX" + ] + } + }, + "next_page_payload": { + "pagination_identifier": "eyJwYWdlTnVtYmVyIjo0fQ==" + } + } + }, + { + "description": "Should return the pagination_identifier validation error", + "input": { + "pagination_identifier": "SGVsbG8sIFdvcm*xkIQ=" + }, + "output": { + "http_code": 400, + "code": "INVALID_PARAMS", + "param_errors": [ + "invalid_pagination_identifier" + ] + } + }, + { + "description": "Should return the error from salesforce", + "input": { + "pagination_identifier": "eyJwYWdlTnVtYmVyIjozfQ==" + }, + "mocks": { + "SalesforceRequestInterfaceError": "Something went wrong" + }, + "output": { + "http_code": 500, + "code": "INTERNAL_SERVER_ERROR" + } + }, + { + "description": "Should return the error while parsing pagination_identifier", + "input": { + "pagination_identifier": "eyJwYWdlTnVtYmVyIjozfQ==" + }, + "mocks": { + "writeValueAsString": "something went wrong" + }, + "output": { + "http_code": 500, + "code": "INTERNAL_SERVER_ERROR" + } + }, + { + "description": "Should return the error while encoding paginationJson", + "input": { + "pagination_identifier": "eyJwYWdlTnVtYmVyIjozfQ==" + }, + "mocks": { + "readValue": "something went wrong" + }, + "output": { + "http_code": 500, + "code": "INTERNAL_SERVER_ERROR" + } + } +] \ No newline at end of file diff --git a/src/test/resources/data/functional/controllers/accountController/getNoteDetails.scenarios.json b/src/test/resources/data/functional/controllers/accountController/getNoteDetails.scenarios.json new file mode 100644 index 00000000..b2359c95 --- /dev/null +++ b/src/test/resources/data/functional/controllers/accountController/getNoteDetails.scenarios.json @@ -0,0 +1,86 @@ +[ + { + "description": "Should return the 5 latest Notes", + "input": { + "accountId":"0011e00000bWSxdAAG", + "noteId":"0691e000001WY58AAG" + }, + "mocks":{ + "makeCompositeRequest":{ + "compositeResponse": [ + { + "body": { + "totalSize": 1, + "done": true, + "records": [ + { + "attributes": { + "type": "ContentNote", + "url": "/services/data/v58.0/sobjects/ContentNote/0691e000001WY58AAG" + }, + "Id": "0691e000001WY58AAG", + "Title": "Test Note with composite", + "TextPreview": "Let's use plural routes and have accountId in URL as per the API Convention.\\n/v1/account/content-notes => /v1/salesforce/accounts/{accountId}/content-notes\\n/v1/composite is generic. It can be under a salesforce route.\\n/v1/composite => /v1/salesforce/co", + "CreatedBy": { + "attributes": { + "type": "User", + "url": "/services/data/v58.0/sobjects/User/0055i00000AUxQHAA1" + }, + "Name": "Ashfaq Bhojani" + }, + "LastModifiedDate": "2023-08-01T10:38:23.000+0000" + } + ] + }, + "httpHeaders": {}, + "httpStatusCode": 200, + "referenceId": "ContentDocument" + } + ] + }, + "makeWrapperRequest":"

Let's use plural routes and have accountId in URL as per the API Convention.\n/v1/account/content-notes => /v1/salesforce/accounts/{accountId}/content-notes\n/v1/composite is generic. It can be under a salesforce route.\n/v1/composite => /v1/salesforce/composite\nQuestion:TextPreview in content notes response is a truncated version of the full text in notes?She suspicion dejection saw instantly. Well deny may real one told yet saw hard dear. Bed chief house rapid right the. Set noisy one state tears which. No girl oh part must fact high my he. Simplicity in excellence melancholy as remarkably discovered. Own partiality motionless was old excellence she inquietude contrasted. Sister giving so wicket cousin of an he rather marked. Of on game part body rich. Adapted mr savings venture it or comfort affixed friends.Prepared is me marianne pleasure likewise debating. Wonder an unable except better stairs do ye admire. His and eat secure sex called esteem praise. So moreover as speedily differed branched ignorant. Tall are her knew poor now does then. Procured to contempt oh he raptures amounted occasion. One boy assure income spirit lovers set.Passage its ten led hearted removal cordial. Preference any astonished unreserved mrs. Prosperous understood middletons in conviction an uncommonly do. Supposing so be resolving breakfast am or perfectly. Is drew am hill from mr. Valley by oh twenty direct me so. Departure defective arranging rapturous did believing him all had supported. Family months lasted simple set nature vulgar him. Picture for attempt joy excited ten carried manners talking how. Suspicion neglected he resolving agreement perceived at an.Dashwood contempt on mr unlocked resolved provided of of. Stanhill wondered it it welcomed oh. Hundred no prudent he however smiling at an offence. If earnestly extremity he he propriety something admitting convinced ye. Pleasant in to although as if differed horrible. Mirth his quick its set front enjoy hoped had there. Who connection imprudence middletons too but increasing celebrated principles joy. Herself too improve gay winding ask expense are compact. New all paid few hard pure she.That know ask case sex ham dear her spot. Weddings followed the all marianne nor whatever settling. Perhaps six prudent several her had offence. Did had way law dinner square tastes. Recommend concealed yet her procuring see consulted depending. Adieu

" + }, + "output":{ + "note_detail": { + "id": "0691e000001WY58AAG", + "creator": "Ashfaq Bhojani", + "text": "\"

Let's use plural routes and have accountId in URL as per the API Convention.\\n/v1/account/content-notes => /v1/salesforce/accounts/{accountId}/content-notes\\n/v1/composite is generic. It can be under a salesforce route.\\n/v1/composite => /v1/salesforce/composite\\nQuestion:TextPreview in content notes response is a truncated version of the full text in notes?She suspicion dejection saw instantly. Well deny may real one told yet saw hard dear. Bed chief house rapid right the. Set noisy one state tears which. No girl oh part must fact high my he. Simplicity in excellence melancholy as remarkably discovered. Own partiality motionless was old excellence she inquietude contrasted. Sister giving so wicket cousin of an he rather marked. Of on game part body rich. Adapted mr savings venture it or comfort affixed friends.Prepared is me marianne pleasure likewise debating. Wonder an unable except better stairs do ye admire. His and eat secure sex called esteem praise. So moreover as speedily differed branched ignorant. Tall are her knew poor now does then. Procured to contempt oh he raptures amounted occasion. One boy assure income spirit lovers set.Passage its ten led hearted removal cordial. Preference any astonished unreserved mrs. Prosperous understood middletons in conviction an uncommonly do. Supposing so be resolving breakfast am or perfectly. Is drew am hill from mr. Valley by oh twenty direct me so. Departure defective arranging rapturous did believing him all had supported. Family months lasted simple set nature vulgar him. Picture for attempt joy excited ten carried manners talking how. Suspicion neglected he resolving agreement perceived at an.Dashwood contempt on mr unlocked resolved provided of of. Stanhill wondered it it welcomed oh. Hundred no prudent he however smiling at an offence. If earnestly extremity he he propriety something admitting convinced ye. Pleasant in to although as if differed horrible. Mirth his quick its set front enjoy hoped had there. Who connection imprudence middletons too but increasing celebrated principles joy. Herself too improve gay winding ask expense are compact. New all paid few hard pure she.That know ask case sex ham dear her spot. Weddings followed the all marianne nor whatever settling. Perhaps six prudent several her had offence. Did had way law dinner square tastes. Recommend concealed yet her procuring see consulted depending. Adieu

\"", + "last_modified_time": "2023-08-01T10:38:23.000+00:00" + } + } + }, + { + "description": "should return error response when note id is invalid", + "input": { + "account_id": "0011e00000bWSxdAAG", + "note_id": "0011e00000bWSxdAAG" + }, + "mocks":{ + "makeCompositeRequest":{ + "compositeResponse": [ + { + "body": { + "totalSize": 0, + "done": true, + "records": [] + }, + "httpHeaders": {}, + "httpStatusCode": 200, + "referenceId": "Note" + } + ] + }, + "makeWrapperRequest":[ + { + "errorCode": "NOT_FOUND", + "message": "The requested resource does not exist" + } + ] + }, + "output": { + "http_code": 500, + "code": "INTERNAL_SERVER_ERROR", + "param_errors": [] + } + } +] \ No newline at end of file diff --git a/src/test/resources/data/controllers/accountController/getNotesList.scenarios.json b/src/test/resources/data/functional/controllers/accountController/getNotesList.scenarios.json similarity index 97% rename from src/test/resources/data/controllers/accountController/getNotesList.scenarios.json rename to src/test/resources/data/functional/controllers/accountController/getNotesList.scenarios.json index 393ed52f..05f54b4b 100644 --- a/src/test/resources/data/controllers/accountController/getNotesList.scenarios.json +++ b/src/test/resources/data/functional/controllers/accountController/getNotesList.scenarios.json @@ -3,7 +3,6 @@ "description": "Should return the 5 latest Notes", "input": { "accountId":"0011e00000bWSxdAAG", - "cookie": "1:00112233445566AAA1:SALESFORCE:1899000000:IbFOqqub193hajGqMDwrF41iUiKl8KJKWpmPl7oqX4eesrydP9cSx483f86OFK+dtSNAukoy+Tr7pRYsD3yVNw==", "documentId": ["0691e000001WKXSAA4","0691e000001WSt7AAG","0691e000001WSuKAAW","0691e000001WSzFAAW","0691e000001WUBCAA4","0691e000001WWHCAA4","0691e000001WXlcAAG","0691e000001WXzPAAW","0691e000001WY0NAAW","0691e000001WY58AAG","0691e000001WaSzAAK","0691e000001Wc4fAAC","0691e000001Wc4pAAC","0691e000001Wl1GAAS","0691e000001WrxGAAS","0691e000001WrxpAAC","0691e000001WrxuAAC","0691e000001Ws2QAAS","0691e000001WsIxAAK","0691e000001WsJHAA0","0691e000001Wt2HAAS","0691e000001Wt5GAAS","0691e000001WtEXAA0","0691e000001WtRgAAK","0691e000001WtxrAAC","0691e000001Wu8pAAC","0691e000001WuIuAAK","0691e000001WuJ9AAK"] }, @@ -366,7 +365,6 @@ "description": "should return empty response when no notes are present", "input": { "account_id": "0011e00000bWSxdAAG", - "cookie": "1:00112233445566AAA1:SALESFORCE:1899000000:IbFOqqub193hajGqMDwrF41iUiKl8KJKWpmPl7oqX4eesrydP9cSx483f86OFK+dtSNAukoy+Tr7pRYsD3yVNw==", "document_id":[] }, "mocks":{ @@ -397,7 +395,6 @@ "description": "should return error response when account id is invalid", "input": { "account_id": "0011e00000bWSxdAAG", - "cookie": "1:00112233445566AAA1:SALESFORCE:1899000000:IbFOqqub193hajGqMDwrF41iUiKl8KJKWpmPl7oqX4eesrydP9cSx483f86OFK+dtSNAukoy+Tr7pRYsD3yVNw==", "document_id":[] }, "mocks":{ diff --git a/src/test/resources/data/functional/controllers/accountNoteController/deleteAccountNote.scenarios.json b/src/test/resources/data/functional/controllers/accountNoteController/deleteAccountNote.scenarios.json new file mode 100644 index 00000000..f9a6f2d1 --- /dev/null +++ b/src/test/resources/data/functional/controllers/accountNoteController/deleteAccountNote.scenarios.json @@ -0,0 +1,59 @@ +[ + { + "description": "Should successfully delete the note for given note id", + "input": { + "accountId": "0011e00000bWSxdAAG", + "noteId":"0691e000001X1yTAAS" + }, + "mocks": { + "makeCompositeRequest": { + "compositeResponse": [ + { + "body": null, + "httpHeaders": {}, + "httpStatusCode": 204, + "referenceId": "DeleteNote" + } + ] + } + }, + "output": {} + }, + { + "description": "should return error response when note id is invalid", + "input": { + "accountId": "0011e00000bWSxdAAG", + "noteId":"invalidNoteId" + }, + "mocks": { + "makeCompositeRequest": { + "compositeResponse": [ + { + "body": [ + { + "errorCode": "NOT_FOUND", + "message": "Provided external ID field does not exist or is not accessible: INVALID_NOTE_ID" + } + ], + "httpHeaders": {}, + "httpStatusCode": 404, + "referenceId": "DeleteNote" + } + ] + } + }, + "output": { + "http_code": 400, + "message": "At least one parameter is invalid or missing.", + "code": "INVALID_PARAMS", + "internal_error_identifier": "l_ca_dan_dasn_pr_1", + "param_errors": [ + { + "parameter": "note_id", + "param_error_identifier": "invalid_note_id", + "message": "The note id you sent is incorrect. Please double check and try again." + } + ] + } + } +] \ No newline at end of file diff --git a/src/test/resources/data/functional/controllers/accountTaskController/createTask.scenarios.json b/src/test/resources/data/functional/controllers/accountTaskController/createTask.scenarios.json new file mode 100644 index 00000000..af6b7e26 --- /dev/null +++ b/src/test/resources/data/functional/controllers/accountTaskController/createTask.scenarios.json @@ -0,0 +1,195 @@ +[ + { + "description": "Should successfully create the note", + "input": { + "body": { + "crm_organization_user_id":"0011e00000bWSxdAA1", + "description":"Update remaining documentations", + "due_date":"2023-12-01" + }, + "accountId": "0011e00000bWSxdAAG" + }, + "mocks": { + "makeCompositeRequest": { + "compositeResponse": [ + { + "body": { + "id": "00T1e000007mDA7EAM", + "success": true, + "errors": [] + }, + "httpHeaders": { + "Location": "/services/data/v58.0/sobjects/Task/00T1e000007mDA7EAM" + }, + "httpStatusCode": 201, + "referenceId": "CreateTask" + } + ] + } + }, + "output": { + "task_id": "00T1e000007mDA7EAM" + } + }, + { + "description": "Should fail when the task description is empty", + "input": { + "body": { + "crm_organization_user_id":"0011e00000bWSxdAA1", + "description":"", + "due_date":"2023-12-01" + }, + "accountId": "0011e00000bWSxdAAG" + }, + "mocks": {}, + "output": { + "http_code": 400, + "message": "At least one parameter is invalid or missing.", + "code": "INVALID_PARAMS", + "internal_error_identifier": "b_2", + "param_errors": [ + "missing_description" + ] + } + }, + { + "description": "Should fail when the task description is not provided", + "input": { + "body": { + "crm_organization_user_id":"0011e00000bWSxdAA1", + "due_date":"2023-12-01" + }, + "accountId": "0011e00000bWSxdAAG" + }, + "mocks": {}, + "output": { + "http_code": 400, + "message": "At least one parameter is invalid or missing.", + "code": "INVALID_PARAMS", + "internal_error_identifier": "b_2", + "param_errors": [ + "missing_description" + ] + } + }, + { + "description": "Should fail when the task due_date is not provided", + "input": { + "body": { + "crm_organization_user_id":"0011e00000bWSxdAA1", + "description":"Update remaining documentations" + }, + "accountId": "0011e00000bWSxdAAG" + }, + "mocks": {}, + "output": { + "http_code": 400, + "message": "At least one parameter is invalid or missing.", + "code": "INVALID_PARAMS", + "internal_error_identifier": "b_2", + "param_errors": [ + "invalid_due_date" + ] + } + }, + { + "description": "Should fail when the task crm_organization_user_id is not provided", + "input": { + "body": { + "description":"Update remaining documentations", + "due_date":"2023-12-01" + }, + "accountId": "0011e00000bWSxdAAG" + }, + "mocks": {}, + "output": { + "http_code": 400, + "message": "At least one parameter is invalid or missing.", + "code": "INVALID_PARAMS", + "internal_error_identifier": "b_2", + "param_errors": [ + "missing_crm_organization_user_id" + ] + } + }, + { + "description": "Should fail when the task Due is not valid", + "input": { + "body": { + "crm_organization_user_id":"0011e00000bWSxdAA1", + "description":"Update remaining documentations", + "due_date":"2023-120-01" + }, + "accountId": "0011e00000bWSxdAAG" + }, + "mocks": {}, + "output": { + "http_code": 400, + "message": "At least one parameter is invalid or missing.", + "code": "INVALID_PARAMS", + "internal_error_identifier": "b_2", + "param_errors": [ + "invalid_due_date" + ] + } + }, + { + "description": "Should fail when the task description is too long", + "input": { + "body": { + "crm_organization_user_id":"0011e00000bWSxdAA1", + "description":"Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum. Nam quam nunc, blandit vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec odio et ante tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. Nullam quis ante. Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed consequat, leo eget bibendum sodales, augue velit cursus nunc, quis gravida magna mi a libero. Fusce vulputate eleifend sapien. Vestibulum purus quam, scelerisque ut, mollis sed, nonummy id, metus. Nullam accumsan lorem in dui. Cras ultricies mi eu turpis hendrerit fringilla. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; In ac dui quis mi consectetuer lacinia. Nam pretium turpis et arcu. Duis arcu tortor, suscipit eget, imperdiet nec, imperdiet iaculis, ipsum. Sed aliquam ultrices mauris. Integer ante arcu, accumsan a, consectetuer eget, posuere ut, mauris. Praesent adipiscing. Phasellus ullamcorper ipsum rutrum nunc. Nunc nonummy metus. Vestibulum volutpat pretium libero. Cras id dui. Aenean ut eros et nisl sagittis vestibulum. Nullam nulla eros, ultricies sit amet, nonummy id, imperdiet feugiat, pede. Sed lectus. Donec mollis hendrerit risus. Phasellus nec sem in justo pellentesque facilisis. Etiam imperdiet imperdiet orci. Nunc nec neque. Phasellus leo dolor, tempus non, auctor et, hendrerit quis, nisi. Curabitur ligula sapien, tincidunt non, euismod vitae, posuere imperdiet, leo. Maecenas malesuada. Praesent congue erat at massa. Sed cursus turpis vitae tortor. Donec posuere vulputate arcu. Phasellus accumsan cursus velit. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed aliquam, nisi quis porttitor congue, elit erat euismod orci, ac placerat dolor lectus quis orci. Phasellus consectetuer vestibulum elit. Aenean tellus metus, bibendum sed, posuere ac, mattis non, nunc. Vestibulum fringilla pede sit amet augue. In turpis. Pellentesque posuere. Praesent turpis. Aenean posuere, tortor sed cursus feugiat, nunc augue blandit nunc, eu sollicitudin urna dolor sagittis lacus. Donec elit libero, sodales nec, volutpat a, suscipit non, turpis. Nullam sagittis. Suspendisse pulvinar, augue ac venenatis condimentum, sem libero volutpat nibh, nec pellentesque velit pede quis nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Fusce id purus. Ut varius tincidunt libero. Phasellus dolor. Maecenas vestibulum mollis diam. Pellentesque ut neque. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. In dui magna, posuere eget, vestibulum et, tempor auctor, justo. In ac felis quis tortor malesuada pretium. Pellentesque auctor neque nec urna. Proin sapien ipsum, porta a, auctor quis, euismod ut, mi. Aenean viverra rhoncus pede. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Ut non enim eleifend felis pretium feugiat. Vivamus quis mi. Phasellus a est. Phasellus magna. In hac habitasse platea dictumst. Curabitur at lacus ac velit ornare lobortis. Curabitur a felis in nunc fringilla tristique. Morbi mattis ullamcorper velit. Phasellus gravida semper nisi. Nullam vel sem. Pellentesque libero tortor, tincidunt et, tincidunt eget, semper nec, quam. Sed hendrerit. Morbi ac felis. Nunc egestas, augue at pellentesque laoreet, felis eros vehicula leo, at malesuada velit leo quis pede. Donec interdum, metus et hendrerit aliquet, dolor diam sagittis ligula, eget egestas libero turpis vel mi. Nunc nulla. Fusce risus nisl, viverra et, tempor et, pretium in, sapien. Donec venenatis vulputate lorem. Morbi nec metus. Phasellus blandit leo ut odio. Maecenas ullamcorper, dui et placerat feugiat, eros pede varius nisi, condimentum viverra felis nunc et lorem. Sed magna purus, fermentum eu, tincidunt eu, varius ut, felis. In auctor lobortis lacus. Quisque libero metus, condimentum nec, tempor a, commodo mollis, magna. Vestibulum ullamcorper mauris at ligula. Fusce fermentum. Nullam cursus lacinia erat. Praesent blandit laoreet nibh. Fusce convallis metus id felis luctus adipiscing. Pellentesque egestas, neque sit amet convallis pulvinar, justo nulla eleifend augue, ac auctor orci leo non est. Quisque id mi. Ut tincidunt tincidunt erat. Etiam feugiat lorem non metus. Vestibulum dapibus nunc ac augue. Curabitur vestibulum aliquam leo. Praesent egestas neque eu enim. In hac habitasse platea dictumst. Fusce a quam. Etiam ut purus mattis mauris sodales aliquam. Curabitur nisi. Quisque malesuada placerat nisl. Nam ipsum risus, rutrum vitae, vestibulum eu, molestie vel, lacus. Sed augue ipsum, egestas nec, vestibulum et, malesuada adipiscing, dui. Vestibulum facilisis, purus nec pulvinar iaculis, ligula mi congue nunc, vitae euismod ligula urna in dolor. Mauris sollicitudin fermentum libero. Praesent nonummy mi in odio. Nunc interdum lacus sit amet orci. Vestibulum rutrum, mi nec elementum vehicula, eros quam gravida nisl, id fringilla neque ante vel mi. Morbi mollis tellus ac sapien. Phasellus volutpat, metus eget egestas mollis, lacus lacus blandit dui, id egestas quam mauris ut lacus. Fusce vel dui. Sed in libero ut nibh placerat accumsan. Proin faucibus arcu quis ante. In consectetuer turpis ut velit. Nulla sit amet est. Praesent metus tellus, elementum eu, semper a, adipiscing nec, purus. Cras risus ipsum, faucibus ut, ullamcorper id, varius ac, leo. Suspendisse feugiat. Suspendisse enim turpis, dictum sed, iaculis a, condimentum nec, nisi. Praesent nec nisl a purus blandit viverra. Praesent ac massa at ligula laoreet iaculis. Nulla neque dolor, sagittis eget, iaculis quis, molestie non, velit. Mauris turpis nunc, blandit et, volutpat molestie, porta ut, ligula. Fusce pharetra convallis urna. Quisque ut nisi. Donec mi odio, faucibus at, scelerisque quis, convallis in, nisi. Suspendisse non nisl sit amet velit hendrerit rutrum. Ut leo. Ut a nisl id ante tempus hendrerit. Proin pretium, leo ac pellentesque mollis, felis nunc ultrices eros, sed gravida augue augue mollis justo. Suspendisse eu ligula. Nulla facilisi. Donec id justo. Praesent porttitor, nulla vitae posuere iaculis, arcu nisl dignissim dolor, a pretium mi sem ut ipsum. Curabitur suscipit suscipit tellus. Praesent vestibulum dapibus nibh. Etiam iaculis nunc ac metus. Ut id nisl quis enim dignissim sagittis. Etiam sollicitudin, ipsum eu pulvinar rutrum, tellus ipsum laoreet sapien, quis venenatis ante odio sit amet eros. Proin magna. Duis vel nibh at velit scelerisque suscipit. Curabitur turpis. Vestibulum suscipit nulla quis orci. Fusce ac felis sit amet ligula pharetra condimentum. Maecenas egestas arcu quis ligula mattis placerat. Duis lobortis massa imperdiet quam. Suspendisse potenti. Pellentesque commodo eros a enim. Vestibulum turpis sem, aliquet eget, lobortis pellentesque, rutrum eu, nisl. Sed libero. Aliquam erat volutpat. Etiam vitae tortor. Morbi vestibulum volutpat enim. Aliquam eu nunc. Nunc sed turpis. Sed mollis, eros et ultrices tempus, mauris ipsum aliquam libero, non adipiscing dolor urna a orci. Nulla porta dolor. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Pellentesque dapibus hendrerit tortor. Praesent egestas tristique nibh. Sed a libero. Cras varius. Donec vitae orci sed dolor rutrum auctor. Fusce egestas elit eget lorem. Suspendisse nisl elit, rhoncus eget, elementum ac, condimentum eget, diam. Nam at tortor in tellus interdum sagittis. Aliquam lobortis. Donec orci lectus, aliquam ut, faucibus non, euismod id, nulla. Curabitur blandit mollis lacus. Nam adipiscing. Vestibulum eu odio. Vivamus laoreet. Nullam tincidunt adipiscing enim. Phasellus tempus. Proin viverra, ligula sit amet ultrices semper, ligula arcu tristique sapien, a accumsan nisi mauris ac eros. Fusce neque. Suspendisse faucibus, nunc et pellentesque egestas, lacus ante convallis tellus, vitae iaculis lacus elit id tortor. Vivamus aliquet elit ac nisl. Fusce fermentum odio nec arcu. Vivamus euismod mauris. In ut quam vitae odio lacinia tincidunt. Praesent ut ligula non mi varius sagittis. Cras sagittis. Praesent ac sem eget est egestas volutpat. Vivamus consectetuer hendrerit lacus. Cras non dolor. Vivamus in erat ut urna cursus vestibulum. Fusce commodo aliquam arcu. Nam commodo suscipit quam. Quisque id odio. Praesent venenatis metus at tortor pulvinar varius. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum. Nam quam nunc, blandit vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec odio et ante tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. Nullam quis ante. Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed consequat, leo eget bibendum sodales, augue velit cursus nunc, quis gravida magna mi a libero. Fusce vulputate eleifend sapien. Vestibulum purus quam, scelerisque ut, mollis sed, nonummy id, metus. Nullam accumsan lorem in dui. Cras ultricies mi eu turpis hendrerit fringilla. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; In ac dui quis mi consectetuer lacinia. Nam pretium turpis et arcu. Duis arcu tortor, suscipit eget, imperdiet nec, imperdiet iaculis, ipsum. Sed aliquam ultrices mauris. Integer ante arcu, accumsan a, consectetuer eget, posuere ut, mauris. Praesent adipiscing. Phasellus ullamcorper ipsum rutrum nunc. Nunc nonummy metus. Vestibulum volutpat pretium libero. Cras id dui. Aenean ut eros et nisl sagittis vestibulum. Nullam nulla eros, ultricies sit amet, nonummy id, imperdiet feugiat, pede. Sed lectus. Donec mollis hendrerit risus. Phasellus nec sem in justo pellentesque facilisis. Etiam imperdiet imperdiet orci. Nunc nec neque. Phasellus leo dolor, tempus non, auctor et, hendrerit quis, nisi. Curabitur ligula sapien, tincidunt non, euismod vitae, posuere imperdiet, leo. Maecenas malesuada. Praesent congue erat at massa. Sed cursus turpis vitae tortor. Donec posuere vulputate arcu. Phasellus accumsan cursus velit. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed aliquam, nisi quis porttitor congue, elit erat euismod orci, ac placerat dolor lectus quis orci. Phasellus consectetuer vestibulum elit. Aenean tellus metus, bibendum sed, posuere ac, mattis non, nunc. Vestibulum fringilla pede sit amet augue. In turpis. Pellentesque posuere. Praesent turpis. Aenean posuere, tortor sed cursus feugiat, nunc augue blandit nunc, eu sollicitudin urna dolor sagittis lacus. Donec elit libero, sodales nec, volutpat a, suscipit non, turpis. Nullam sagittis. Suspendisse pulvinar, augue ac venenatis condimentum, sem libero volutpat nibh, nec pellentesque velit pede quis nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Fusce id purus. Ut varius tincidunt libero. Phasellus dolor. Maecenas vestibulum mollis diam. Pellentesque ut neque. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. In dui magna, posuere eget, vestibulum et, tempor auctor, justo. In ac felis quis tortor malesuada pretium. Pellentesque auctor neque nec urna. Proin sapien ipsum, porta a, auctor quis, euismod ut, mi. Aenean viverra rhoncus pede. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Ut non enim eleifend felis pretium feugiat. Vivamus quis mi. Phasellus a est. Phasellus magna. In hac habitasse platea dictumst. Curabitur at lacus ac velit ornare lobortis. Curabitur a felis in nunc fringilla tristique. Morbi mattis ullamcorper velit. Phasellus gravida semper nisi. Nullam vel sem. Pellentesque libero tortor, tincidunt et, tincidunt eget, semper nec, quam. Sed hendrerit. Morbi ac felis. Nunc egestas, augue at pellentesque laoreet, felis eros vehicula leo, at malesuada velit leo quis pede. Donec interdum, metus et hendrerit aliquet, dolor diam sagittis ligula, eget egestas libero turpis vel mi. Nunc nulla. Fusce risus nisl, viverra et, tempor et, pretium in, sapien. Donec venenatis vulputate lorem. Morbi nec metus. Phasellus blandit leo ut odio. Maecenas ullamcorper, dui et placerat feugiat, eros pede varius nisi, condimentum viverra felis nunc et lorem. Sed magna purus, fermentum eu, tincidunt eu, varius ut, felis. In auctor lobortis lacus. Quisque libero metus, condimentum nec, tempor a, commodo mollis, magna. Vestibulum ullamcorper mauris at ligula. Fusce fermentum. Nullam cursus lacinia erat. Praesent blandit laoreet nibh. Fusce convallis metus id felis luctus adipiscing. Pellentesque egestas, neque sit amet convallis pulvinar, justo nulla eleifend augue, ac auctor orci leo non est. Quisque id mi. Ut tincidunt tincidunt erat. Etiam feugiat lorem non metus. Vestibulum dapibus nunc ac augue. Curabitur vestibulum aliquam leo. Praesent egestas neque eu enim. In hac habitasse platea dictumst. Fusce a quam. Etiam ut purus mattis mauris sodales aliquam. Curabitur nisi. Quisque malesuada placerat nisl. Nam ipsum risus, rutrum vitae, vestibulum eu, molestie vel, lacus. Sed augue ipsum, egestas nec, vestibulum et, malesuada adipiscing, dui. Vestibulum facilisis, purus nec pulvinar iaculis, ligula mi congue nunc, vitae euismod ligula urna in dolor. Mauris sollicitudin fermentum libero. Praesent nonummy mi in odio. Nunc interdum lacus sit amet orci. Vestibulum rutrum, mi nec elementum vehicula, eros quam gravida nisl, id fringilla neque ante vel mi. Morbi mollis tellus ac sapien. Phasellus volutpat, metus eget egestas mollis, lacus lacus blandit dui, id egestas quam mauris ut lacus. Fusce vel dui. Sed in libero ut nibh placerat accumsan. Proin faucibus arcu quis ante. In consectetuer turpis ut velit. Nulla sit amet est. Praesent metus tellus, elementum eu, semper a, adipiscing nec, purus. Cras risus ipsum, faucibus ut, ullamcorper id, varius ac, leo. Suspendisse feugiat. Suspendisse enim turpis, dictum sed, iaculis a, condimentum nec, nisi. Praesent nec nisl a purus blandit viverra. Praesent ac massa at ligula laoreet iaculis. Nulla neque dolor, sagittis eget, iaculis quis, molestie non, velit. Mauris turpis nunc, blandit et, volutpat molestie, porta ut, ligula. Fusce pharetra convallis urna. Quisque ut nisi. Donec mi odio, faucibus at, scelerisque quis, convallis in, nisi. Suspendisse non nisl sit amet velit hendrerit rutrum. Ut leo. Ut a nisl id ante tempus hendrerit. Proin pretium, leo ac pellentesque mollis, felis nunc ultrices eros, sed gravida augue augue mollis justo. Suspendisse eu ligula. Nulla facilisi. Donec id justo. Praesent porttitor, nulla vitae posuere iaculis, arcu nisl dignissim dolor, a pretium mi sem ut ipsum. Curabitur suscipit suscipit tellus. Praesent vestibulum dapibus nibh. Etiam iaculis nunc ac metus. Ut id nisl quis enim dignissim sagittis. Etiam sollicitudin, ipsum eu pulvinar rutrum, tellus ipsum laoreet sapien, quis venenatis ante odio sit amet eros. Proin magna. Duis vel nibh at velit scelerisque suscipit. Curabitur turpis. Vestibulum suscipit nulla quis orci. Fusce ac felis sit amet ligula pharetra condimentum. Maecenas egestas arcu quis ligula mattis placerat. Duis lobortis massa imperdiet quam. Suspendisse potenti. Pellentesque commodo eros a enim. Vestibulum turpis sem, aliquet eget, lobortis pellentesque, rutrum eu, nisl. Sed libero. Aliquam erat volutpat. Etiam vitae tortor. Morbi vestibulum volutpat enim. Aliquam eu nunc. Nunc sed turpis. Sed mollis, eros et ultrices tempus, mauris ipsum aliquam libero, non adipiscing dolor urna a orci. Nulla porta dolor. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Pellentesque dapibus hendrerit tortor. Praesent egestas tristique nibh. Sed a libero. Cras varius. Donec vitae orci sed dolor rutrum auctor. Fusce egestas elit eget lorem. Suspendisse nisl elit, rhoncus eget, elementum ac, condimentum eget, diam. Nam at tortor in tellus interdum sagittis. Aliquam lobortis. Donec orci lectus, aliquam ut, faucibus non, euismod id, nulla. Curabitur blandit mollis lacus. Nam adipiscing. Vestibulum eu odio. Vivamus laoreet. Nullam tincidunt adipiscing enim. Phasellus tempus. Proin viverra, ligula sit amet ultrices semper, ligula arcu tristique sapien, a accumsan nisi mauris ac eros. Fusce neque. Suspendisse faucibus, nunc et pellentesque egestas, lacus ante convallis tellus, vitae iaculis lacus elit id tortor. Vivamus aliquet elit ac nisl. Fusce fermentum odio nec arcu. Vivamus euismod mauris. In ut quam vitae odio lacinia tincidunt. Praesent ut ligula non mi varius sagittis. Cras sagittis. Praesent ac sem eget est egestas volutpat. Vivamus consectetuer hendrerit lacus. Cras non dolor. Vivamus in erat ut urna cursus vestibulum. Fusce commodo aliquam arcu. Nam commodo suscipit quam. Quisque id odio. Praesent venenatis metus at tortor pulvinar varius. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum. Nam quam nunc, blandit vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec odio et ante tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. Nullam quis ante. Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed consequat, leo eget bibendum sodales, augue velit cursus nunc, quis gravida magna mi a libero. Fusce vulputate eleifend sapien. Vestibulum purus quam, scelerisque ut, mollis sed, nonummy id, metus. Nullam accumsan lorem in dui. Cras ultricies mi eu turpis hendrerit fringilla. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; In ac dui quis mi consectetuer lacinia. Nam pretium turpis et arcu. Duis arcu tortor, suscipit eget, imperdiet nec, imperdiet iaculis, ipsum. Sed aliquam ultrices mauris. Integer ante arcu, accumsan a, consectetuer eget, posuere ut, mauris. Praesent adipiscing. Phasellus ullamcorper ipsum rutrum nunc. Nunc nonummy metus. Vestibulum volutpat pretium libero. Cras id dui. Aenean ut eros et nisl sagittis vestibulum. Nullam nulla eros, ultricies sit amet, nonummy id, imperdiet feugiat, pede. Sed lectus. Donec mollis hendrerit risus. Phasellus nec sem in justo pellentesque facilisis. Etiam imperdiet imperdiet orci. Nunc nec neque. Phasellus leo dolor, tempus non, auctor et, hendrerit quis, nisi. Curabitur ligula sapien, tincidunt non, euismod vitae, posuere imperdiet, leo. Maecenas malesuada. Praesent congue erat at massa. Sed cursus turpis vitae tortor. Donec posuere vulputate arcu. Phasellus accumsan cursus velit. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed aliquam, nisi quis porttitor congue, elit erat euismod orci, ac placerat dolor lectus quis orci. Phasellus consectetuer vestibulum elit. Aenean tellus metus, bibendum sed, posuere ac, mattis non, nunc. Vestibulum fringilla pede sit amet augue. In turpis. Pellentesque posuere. Praesent turpis. Aenean posuere, tortor sed cursus feugiat, nunc augue blandit nunc, eu sollicitudin urna dolor sagittis lacus. Donec elit libero, sodales nec, volutpat a, suscipit non, turpis. Nullam sagittis. Suspendisse pulvinar, augue ac venenatis condimentum, sem libero volutpat nibh, nec pellentesque velit pede quis nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Fusce id purus. Ut varius tincidunt libero. Phasellus dolor. Maecenas vestibulum mollis diam. Pellentesque ut neque. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. In dui magna, posuere eget, vestibulum et, tempor auctor, justo. In ac felis quis tortor malesuada pretium. Pellentesque auctor neque nec urna. Proin sapien ipsum, porta a, auctor quis, euismod ut, mi. Aenean viverra rhoncus pede. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Ut non enim eleifend felis pretium feugiat. Vivamus quis mi. Phasellus a est. Phasellus magna. In hac habitasse platea dictumst. Curabitur at lacus ac velit ornare lobortis. Curabitur a felis in nunc fringilla tristique. Morbi mattis ullamcorper velit. Phasellus gravida semper nisi. Nullam vel sem. Pellentesque libero tortor, tincidunt et, tincidunt eget, semper nec, quam. Sed hendrerit. Morbi ac felis. Nunc egestas, augue at pellentesque laoreet, felis eros vehicula leo, at malesuada velit leo quis pede. Donec interdum, metus et hendrerit aliquet, dolor diam sagittis ligula, eget egestas libero turpis vel mi. Nunc nulla. Fusce risus nisl, viverra et, tempor et, pretium in, sapien. Donec venenatis vulputate lorem. Morbi nec metus. Phasellus blandit leo ut odio. Maecenas ullamcorper, dui et placerat feugiat, eros pede varius nisi, condimentum viverra felis nunc et lorem. Sed magna purus, fermentum eu, tincidunt eu, varius ut, felis. In auctor lobortis lacus. Quisque libero metus, condimentum nec, tempor a, commodo mollis, magna. Vestibulum ullamcorper mauris at ligula. Fusce fermentum. Nullam cursus lacinia erat. Praesent blandit laoreet nibh. Fusce convallis metus id felis luctus adipiscing. Pellentesque egestas, neque sit amet convallis pulvinar, justo nulla eleifend augue, ac auctor orci leo non est. Quisque id mi. Ut tincidunt tincidunt erat. Etiam feugiat lorem non metus. Vestibulum dapibus nunc ac augue. Curabitur vestibulum aliquam leo. Praesent egestas neque eu enim. In hac habitasse platea dictumst. Fusce a quam. Etiam ut purus mattis mauris sodales aliquam. Curabitur nisi. Quisque malesuada placerat nisl. Nam ipsum risus, rutrum vitae, vestibulum eu, molestie vel, lacus. Sed augue ipsum, egestas nec, vestibulum et, malesuada adipiscing, dui. Vestibulum facilisis, purus nec pulvinar iaculis, ligula mi congue nunc, vitae euismod ligula urna in dolor. Mauris sollicitudin fermentum libero. Praesent nonummy mi in odio. Nunc interdum lacus sit amet orci. Vestibulum rutrum, mi nec elementum vehicula, eros quam gravida nisl, id fringilla neque ante vel mi. Morbi mollis tellus ac sapien. Phasellus volutpat, metus eget egestas mollis, lacus lacus blandit dui, id egestas quam mauris ut lacus. Fusce vel dui. Sed in libero ut nibh placerat accumsan. Proin faucibus arcu quis ante. In consectetuer turpis ut velit. Nulla sit amet est. Praesent metus tellus, elementum eu, semper a, adipiscing nec, purus. Cras risus ipsum, faucibus ut, ullamcorper id, varius ac, leo. Suspendisse feugiat. Suspendisse enim turpis, dictum sed, iaculis a, condimentum nec, nisi. Praesent nec nisl a purus blandit viverra. Praesent ac massa at ligula laoreet iaculis. Nulla neque dolor, sagittis eget, iaculis quis, molestie non, velit. Mauris turpis nunc, blandit et, volutpat molestie, porta ut, ligula. Fusce pharetra convallis urna. Quisque ut nisi. Donec mi odio, faucibus at, scelerisque quis, convallis in, nisi. Suspendisse non nisl sit amet velit hendrerit rutrum. Ut leo. Ut a nisl id ante tempus hendrerit. Proin pretium, leo ac pellentesque mollis, felis nunc ultrices eros, sed gravida augue augue mollis justo. Suspendisse eu ligula. Nulla facilisi. Donec id justo. Praesent porttitor, nulla vitae posuere iaculis, arcu nisl dignissim dolor, a pretium mi sem ut ipsum. Curabitur suscipit suscipit tellus. Praesent vestibulum dapibus nibh. Etiam iaculis nunc ac metus. Ut id nisl quis enim dignissim sagittis. Etiam sollicitudin, ipsum eu pulvinar rutrum, tellus ipsum laoreet sapien, quis venenatis ante odio sit amet eros. Proin magna. Duis vel nibh at velit scelerisque suscipit. Curabitur turpis. Vestibulum suscipit nulla quis orci. Fusce ac felis sit amet ligula pharetra condimentum. Maecenas egestas arcu quis ligula mattis placerat. Duis lobortis massa imperdiet quam. Suspendisse potenti. Pellentesque commodo eros a enim. Vestibulum turpis sem, aliquet eget, lobortis pellentesque, rutrum eu, nisl. Sed libero. Aliquam erat volutpat. Etiam vitae tortor. Morbi vestibulum volutpat enim. Aliquam eu nunc. Nunc sed turpis. Sed mollis, eros et ultrices tempus, mauris ipsum aliquam libero, non adipiscing dolor urna a orci. Nulla porta dolor. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Pellentesque dapibus hendrerit tortor. Praesent egestas tristique nibh. Sed a libero. Cras varius. Donec vitae orci sed dolor rutrum auctor. Fusce egestas elit eget lorem. Suspendisse nisl elit, rhoncus eget, elementum ac, condimentum eget, diam. Nam at tortor in tellus interdum sagittis. Aliquam lobortis. Donec orci lectus, aliquam ut, faucibus non, euismod id, nulla. Curabitur blandit mollis lacus. Nam adipiscing. Vestibulum eu odio. Vivamus laoreet. Nullam tincidunt adipiscing enim. Phasellus tempus. Proin viverra, ligula sit amet ultrices semper, ligula arcu tristique sapien, a accumsan nisi mauris ac eros. Fusce neque. Suspendisse faucibus, nunc et pellentesque egestas, lacus ante convallis tellus, vitae iaculis lacus elit id tortor. Vivamus aliquet elit ac nisl. Fusce fermentum odio nec arcu. Vivamus euismod mauris. In ut quam vitae odio lacinia tincidunt. Praesent ut ligula non mi varius sagittis. Cras sagittis. Praesent ac sem eget est egestas volutpat. Vivamus consectetuer hendrerit lacus. Cras non dolor. Vivamus in erat ut urna cursus vestibulum. Fusce commodo aliquam arcu. Nam commodo suscipit quam. Quisque id odio. Praesent venenatis metus at tortor pulvinar varius. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum. Nam quam nunc, blandit vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec odio et ante tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. Nullam quis ante. Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed consequat, leo eget bibendum sodales, augue velit cursus nunc, quis gravida magna mi a libero. Fusce vulputate eleifend sapien. Vestibulum purus quam, scelerisque ut, mollis sed, nonummy id, metus. Nullam accumsan lorem in dui. Cras ultricies mi eu turpis hendrerit fringilla. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; In ac dui quis mi consectetuer lacinia. Nam pretium turpis et arcu. Duis arcu tortor, suscipit eget, imperdiet nec, imperdiet iaculis, ipsum. Sed aliquam ultrices mauris. Integer ante arcu, accumsan a, consectetuer eget, posuere ut, mauris. Praesent adipiscing. Phasellus ullamcorper ipsum rutrum nunc. Nunc nonummy metus. Vestibulum volutpat pretium libero. Cras id dui. Aenean ut eros et nisl sagittis vestibulum. Nullam nulla eros, ultricies sit amet, nonummy id, imperdiet feugiat, pede. Sed lectus. Donec mollis hendrerit risus. Phasellus nec sem in justo pellentesque facilisis. Etiam imperdiet imperdiet orci. Nunc nec neque. Phasellus leo dolor, tempus non, auctor et, hendrerit quis, nisi. Curabitur ligula sapien, tincidunt non, euismod vitae, posuere imperdiet, leo. Maecenas malesuada. Praesent congue erat at massa. Sed cursus turpis vitae tortor. Donec posuere vulputate arcu. Phasellus accumsan cursus velit. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed aliquam, nisi quis porttitor congue, elit erat euismod orci, ac placerat dolor lectus quis orci. Phasellus consectetuer vestibulum elit. Aenean tellus metus, bibendum sed, posuere ac, mattis non, nunc. Vestibulum fringilla pede sit amet augue. In turpis. Pellentesque posuere. Praesent turpis. Aenean posuere, tortor sed cursus feugiat, nunc augue blandit nunc, eu sollicitudin urna dolor sagittis lacus. Donec elit libero, sodales nec, volutpat a, suscipit non, turpis. Nullam sagittis. Suspendisse pulvinar, augue ac venenatis condimentum, sem libero volutpat nibh, nec pellentesque velit pede quis nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Fusce id purus. Ut varius tincidunt libero. Phasellus dolor. Maecenas vestibulum mollis diam. Pellentesque ut neque. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. In dui magna, posuere eget, vestibulum et, tempor auctor, justo. In ac felis quis tortor malesuada pretium. Pellentesque auctor neque nec urna. Proin sapien ipsum, porta a, auctor quis, euismod ut, mi. Aenean viverra rhoncus pede. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Ut non enim eleifend felis pretium feugiat. Vivamus quis mi. Phasellus a est. Phasellus magna. In hac habitasse platea dictumst. Curabitur at lacus ac velit ornare lobortis. Curabitur a felis in nunc fringilla tristique. Morbi mattis ullamcorper velit. Phasellus gravida semper nisi. Nullam vel sem. Pellentesque libero tortor, tincidunt et, tincidunt eget, semper nec, quam. Sed hendrerit. Morbi ac felis. Nunc egestas, augue at pellentesque laoreet, felis eros vehicula leo, at malesuada velit leo quis pede. Donec interdum, metus et hendrerit aliquet, dolor diam sagittis ligula, eget egestas libero turpis vel mi. Nunc nulla. Fusce risus nisl, viverra et, tempor et, pretium in, sapien. Donec venenatis vulputate lorem. Morbi nec metus. Phasellus blandit leo ut odio. Maecenas ullamcorper, dui et placerat feugiat, eros pede varius nisi, condimentum viverra felis nunc et lorem. Sed magna purus, fermentum eu, tincidunt eu,", + "due_date":"2023-12-01" + }, + "accountId": "0011e00000bWSxdAAG" + }, + "mocks": {}, + "output": { + "http_code": 400, + "message": "At least one parameter is invalid or missing.", + "code": "INVALID_PARAMS", + "internal_error_identifier": "b_2", + "param_errors": [ + "description_too_long" + ] + } + }, + { + "description": "Should fail when the account id invalid", + "input": { + "body": { + "crm_organization_user_id":"0011e00000bWSxdAA1", + "description":"Update remaining documentations", + "due_date":"2023-12-01" + }, + "accountId": "invalid" + }, + "mocks": { + "makeCompositeRequest": { + "compositeResponse": [ + { + "body": [ + { + "message": "Related To ID: id value of incorrect type: 0011e00000bWSxdAA1", + "errorCode": "MALFORMED_ID", + "fields": [ + "WhatId" + ] + } + ], + "httpHeaders": {}, + "httpStatusCode": 400, + "referenceId": "refTask" + } + ] + } + }, + "output": { + "http_code": 500, + "message": "Something went wrong.", + "code": "INTERNAL_SERVER_ERROR", + "internal_error_identifier": "l_ca_ct_cst_1", + "param_errors": [] + } + } + ] \ No newline at end of file diff --git a/src/test/resources/data/functional/controllers/accountTaskController/deleteAccountTask.scenarios.json b/src/test/resources/data/functional/controllers/accountTaskController/deleteAccountTask.scenarios.json new file mode 100644 index 00000000..33f9fbac --- /dev/null +++ b/src/test/resources/data/functional/controllers/accountTaskController/deleteAccountTask.scenarios.json @@ -0,0 +1,55 @@ +[ + { + "description": "Should successfully delete the task for given task id", + "input": { + "accountId": "0011e00000bWSxdAAG", + "taskId":"0691e000001X1yTAAS" + }, + "mocks": { + "makeCompositeRequest": { + "compositeResponse": [ + { + "body": null, + "httpHeaders": {}, + "httpStatusCode": 204, + "referenceId": "DeleteTask" + } + ] + } + }, + "output": {} + }, + { + "description": "should return error response when task id is invalid", + "input": { + "accountId": "0011e00000bWSxdAAG", + "taskId":"invalidTaskId" + }, + "mocks": { + "makeCompositeRequest": { + "compositeResponse": [ + { + "body": [ + { + "errorCode": "NOT_FOUND", + "message": "Provided external ID field does not exist or is not accessible: INVALID_TASK_ID" + } + ], + "httpHeaders": {}, + "httpStatusCode": 404, + "referenceId": "DeleteTask" + } + ] + } + }, + "output": { + "http_code": 400, + "message": "At least one parameter is invalid or missing.", + "code": "INVALID_PARAMS", + "internal_error_identifier": "l_ca_dan_dasn_pr_1", + "param_errors": [ + "invalid_task_id" + ] + } + } + ] \ No newline at end of file diff --git a/src/test/resources/data/functional/controllers/accountTaskController/getAccountTasksList.scenarios.json b/src/test/resources/data/functional/controllers/accountTaskController/getAccountTasksList.scenarios.json new file mode 100644 index 00000000..0d1e6afc --- /dev/null +++ b/src/test/resources/data/functional/controllers/accountTaskController/getAccountTasksList.scenarios.json @@ -0,0 +1,258 @@ +[ + { + "description": "Should return the 5 latest Notes", + "input": { + "accountId": "0011e00000bWSxdAAG" + }, + "mocks": { + "makeCompositeRequest": { + "compositeResponse": [ + { + "body": { + "totalSize": 5, + "done": true, + "records": [ + { + "attributes": { + "type": "Task", + "url": "/services/data/v58.0/sobjects/Task/00T1e000007mGYaEAM" + }, + "Id": "00T1e000007mGYaEAM", + "Description": "Update remaining documentations", + "ActivityDate": "2023-12-01", + "CreatedBy": { + "attributes": { + "type": "User", + "url": "/services/data/v58.0/sobjects/User/0055i00000AUxQHAA1" + }, + "Name": "Name2" + }, + "Owner": { + "attributes": { + "type": "Name", + "url": "/services/data/v58.0/sobjects/User/0051e0000047G03AAE" + }, + "Name": "Name1" + }, + "LastModifiedDate": "2023-08-24T12:16:05.000+0000" + }, + { + "attributes": { + "type": "Task", + "url": "/services/data/v58.0/sobjects/Task/00T1e000007mGXXEA2" + }, + "Id": "00T1e000007mGXXEA2", + "Description": "Update remaining documentations", + "ActivityDate": "2023-12-01", + "CreatedBy": { + "attributes": { + "type": "User", + "url": "/services/data/v58.0/sobjects/User/0055i00000AUxQHAA1" + }, + "Name": "Name2" + }, + "Owner": { + "attributes": { + "type": "Name", + "url": "/services/data/v58.0/sobjects/User/0051e0000047G03AAE" + }, + "Name": "Name1" + }, + "LastModifiedDate": "2023-08-24T12:09:51.000+0000" + }, + { + "attributes": { + "type": "Task", + "url": "/services/data/v58.0/sobjects/Task/00T1e000007mF0oEAE" + }, + "Id": "00T1e000007mF0oEAE", + "Description": "Update remaining documentations", + "ActivityDate": "2023-12-01", + "CreatedBy": { + "attributes": { + "type": "User", + "url": "/services/data/v58.0/sobjects/User/0055i00000AUxQHAA1" + }, + "Name": "Name2" + }, + "Owner": { + "attributes": { + "type": "Name", + "url": "/services/data/v58.0/sobjects/User/0051e0000047G03AAE" + }, + "Name": "Name1" + }, + "LastModifiedDate": "2023-08-23T13:11:03.000+0000" + }, + { + "attributes": { + "type": "Task", + "url": "/services/data/v58.0/sobjects/Task/00T1e000007mEk9EAE" + }, + "Id": "00T1e000007mEk9EAE", + "Description": "Update remaining documentations", + "ActivityDate": "2023-12-01", + "CreatedBy": { + "attributes": { + "type": "User", + "url": "/services/data/v58.0/sobjects/User/0055i00000AUxQHAA1" + }, + "Name": "Name2" + }, + "Owner": { + "attributes": { + "type": "Name", + "url": "/services/data/v58.0/sobjects/User/0051e0000047G03AAE" + }, + "Name": "Name1" + }, + "LastModifiedDate": "2023-08-23T07:49:23.000+0000" + }, + { + "attributes": { + "type": "Task", + "url": "/services/data/v58.0/sobjects/Task/00T1e000007mEfuEAE" + }, + "Id": "00T1e000007mEfuEAE", + "Description": "Update remaining documentations", + "ActivityDate": "2023-12-01", + "CreatedBy": { + "attributes": { + "type": "User", + "url": "/services/data/v58.0/sobjects/User/0055i00000AUxQHAA1" + }, + "Name": "Name2" + }, + "Owner": { + "attributes": { + "type": "Name", + "url": "/services/data/v58.0/sobjects/User/0051e0000047G03AAE" + }, + "Name": "Name1" + }, + "LastModifiedDate": "2023-08-23T07:10:53.000+0000" + } + ] + }, + "httpHeaders": {}, + "httpStatusCode": 200, + "referenceId": "GetTasksList" + } + ] + } + }, + "output": { + "task_ids": [ + "00T1e000007mGYaEAM", + "00T1e000007mGXXEA2", + "00T1e000007mF0oEAE", + "00T1e000007mEk9EAE", + "00T1e000007mEfuEAE" + ], + "task_map_by_id": { + "00T1e000007mGXXEA2": { + "id": "00T1e000007mGXXEA2", + "creator_name": "Name2", + "description": "Update remaining documentations", + "due_date": "2023-12-01", + "crm_organization_user_name": "Name1", + "last_modified_time": "2023-08-24T12:09:51.000+00:00" + }, + "00T1e000007mEk9EAE": { + "id": "00T1e000007mEk9EAE", + "creator_name": "Name2", + "description": "Update remaining documentations", + "due_date": "2023-12-01", + "crm_organization_user_name": "Name1", + "last_modified_time": "2023-08-23T07:49:23.000+00:00" + }, + "00T1e000007mEfuEAE": { + "id": "00T1e000007mEfuEAE", + "creator_name": "Name2", + "description": "Update remaining documentations", + "due_date": "2023-12-01", + "crm_organization_user_name": "Name1", + "last_modified_time": "2023-08-23T07:10:53.000+00:00" + }, + "00T1e000007mF0oEAE": { + "id": "00T1e000007mF0oEAE", + "creator_name": "Name2", + "description": "Update remaining documentations", + "due_date": "2023-12-01", + "crm_organization_user_name": "Name1", + "last_modified_time": "2023-08-23T13:11:03.000+00:00" + }, + "00T1e000007mGYaEAM": { + "id": "00T1e000007mGYaEAM", + "creator_name": "Name2", + "description": "Update remaining documentations", + "due_date": "2023-12-01", + "crm_organization_user_name": "Name1", + "last_modified_time": "2023-08-24T12:16:05.000+00:00" + } + } + } + }, + { + "description": "should return empty response when no tasks are present", + "input": { + "accountId": "0011e00000bWSxdAAG" + }, + "mocks": { + "makeCompositeRequest": { + "compositeResponse": [ + { + "body": { + "totalSize": 0, + "done": true, + "records": [] + }, + "httpHeaders": {}, + "httpStatusCode": 200, + "referenceId": "GetTasksList" + } + ] + } + }, + "output": { + "task_ids": [], + "task_map_by_id": {} + } + }, + { + "description": "should return error response when account id is invalid", + "input": { + "accountId": "invalid" + }, + "mocks": { + "makeCompositeRequest": { + "compositeResponse": [ + { + "body": [ + { + "message": "\nLastModifiedDate FROM Task WHERE WhatId='invalid' ORDER BY LastModifiedDate\n ^\nERROR at Row:1:Column:109\ninvalid ID field: abcd", + "errorCode": "INVALID_QUERY_FILTER_OPERATOR" + } + ], + "httpHeaders": {}, + "httpStatusCode": 400, + "referenceId": "GetTasksList" + } + ] + } + }, + "output": { + "http_code": 400, + "message": "At least one parameter is invalid or missing.", + "code": "INVALID_PARAMS", + "internal_error_identifier": "l_ca_gatl_gsatl_pr_1", + "param_errors": [ + { + "parameter": "account_id", + "param_error_identifier": "invalid_account_id", + "message": "The account id you sent is incorrect. Please double check and try again." + } + ] + } + } +] \ No newline at end of file diff --git a/src/test/resources/data/functional/controllers/authController/Disconnect.scenarios.json b/src/test/resources/data/functional/controllers/authController/Disconnect.scenarios.json new file mode 100644 index 00000000..07eca2cc --- /dev/null +++ b/src/test/resources/data/functional/controllers/authController/Disconnect.scenarios.json @@ -0,0 +1,27 @@ +{ + "testPostDisconnectSuccess": [ + { + "description": "Should disconnect user for valid cookie", + "input": {}, + "mocks": { + "revokeTokens": {} + }, + "output": { + "success": true + } + } + ], + "testPostDisconnectNoTokens": [ + { + "description": "Should return error if no data present ", + "input": {}, + "mocks": { + "revokeTokens": {} + }, + "output": { + "http_code": 500, + "code": "INTERNAL_SERVER_ERROR" + } + } + ] +} \ No newline at end of file diff --git a/src/test/resources/data/controllers/authController/Logout.scenarios.json b/src/test/resources/data/functional/controllers/authController/Logout.scenarios.json similarity index 63% rename from src/test/resources/data/controllers/authController/Logout.scenarios.json rename to src/test/resources/data/functional/controllers/authController/Logout.scenarios.json index fbe4272d..6b46c398 100644 --- a/src/test/resources/data/controllers/authController/Logout.scenarios.json +++ b/src/test/resources/data/functional/controllers/authController/Logout.scenarios.json @@ -3,13 +3,13 @@ { "description": "Should logout user if valid cookie", "input": { - "cookie": "1:00112233445566AAA1:SALESFORCE:1899000000:IbFOqqub193hajGqMDwrF41iUiKl8KJKWpmPl7oqX4eesrydP9cSx483f86OFK+dtSNAukoy+Tr7pRYsD3yVNw==" + "cookie": "1:0055i00000AUxQHAA1:SALESFORCE:app:1692878227:a2xOxjcPChK/YsKCWRuY50J3gH21/tqcGlv7sWU/aPw3/UFVFPbvWn2LLzY5B5kVPXjj1gELRqa4B4ds6uuqIw==" } }, { "description": "Should return error if invalid cookie", "input": { - "cookie": "1:00112233445566AAA1:SALESFORCE:1899000000:invalid" + "cookie": "1:0055i00000AUxQHAA1:SALESFORCE:app:1899000000:invalid" }, "output": { "http_code": 401, @@ -27,7 +27,7 @@ { "description": "Should return error if expired cookie", "input": { - "cookie": "1:00112233445566AAA1:SALESFORCE:1592189876:IbFOqqub193hajGqMDwrF41iUiKl8KJKWpmPl7oqX4eesrydP9cSx483f86OFK+dtSNAukoy+Tr7pRYsD3yVNw==" + "cookie": "1:0055i00000AUxQHAA1:SALESFORCE:app:1592189876:IbFOqqub193hajGqMDwrF41iUiKl8KJKWpmPl7oqX4eesrydP9cSx483f86OFK+dtSNAukoy+Tr7pRYsD3yVNw==" }, "output": { "http_code": 401, diff --git a/src/test/resources/data/controllers/authController/SalesforceConnect.scenarios.json b/src/test/resources/data/functional/controllers/authController/SalesforceConnect.scenarios.json similarity index 71% rename from src/test/resources/data/controllers/authController/SalesforceConnect.scenarios.json rename to src/test/resources/data/functional/controllers/authController/SalesforceConnect.scenarios.json index 53937670..4bd25546 100644 --- a/src/test/resources/data/controllers/authController/SalesforceConnect.scenarios.json +++ b/src/test/resources/data/functional/controllers/authController/SalesforceConnect.scenarios.json @@ -16,14 +16,14 @@ "scope": "xyz", "id_token": "xyz", "instance_url": "xyz", - "id": "https://test.salesforce.com/id/00D2v0000012gJjEAI/00112233445566AAA14", + "id": "https://test.salesforce.com/id/000Org-id/00112233445566AAA14", "token_type": "Bearer", "issued_at": "1690520247198" }, "getIdentity": { - "sub": "https://test.salesforce.com/id/00D2v0000012gJjEAI/00112233445566AAA14", + "sub": "https://test.salesforce.com/id/000Org-id/00112233445566AAA14", "user_id": "00112233445566AAA14", - "organization_id": "00D2v0000012gJjEAI", + "organization_id": "000Org-id", "name": "Test User", "email": "test@test.com" } @@ -106,21 +106,21 @@ "scope": "testScope", "id_token": "testIdToken", "instance_url": "https://test.sandbox.my.salesforce.com", - "id": "https://test.salesforce.com/id/00D2v0000012gJjEAI/00112233445566AAA1", + "id": "https://test.salesforce.com/id/000Org-id/0055i00000AUxQHAA1", "token_type": "Bearer", "issued_at": "1690520247198" }, "getIdentity": { - "sub": "https://test.salesforce.com/id/00D2v0000012gJjEAI/00112233445566AAA1", - "user_id": "00112233445566AAA1", - "organization_id": "00D2v0000012gJjEAI", + "sub": "https://test.salesforce.com/id/000Org-id/0055i00000AUxQHAA1", + "user_id": "0055i00000AUxQHAA1", + "organization_id": "000Org-id", "name": "Test User", "email": "test@test.com" } }, "output": { "current_user": { - "id": "SALESFORCE-00112233445566AAA1", + "id": "SALESFORCE-0055i00000AUxQHAA1", "name": "Test User", "email": "test@test.com" } @@ -160,5 +160,43 @@ "message": "Forbidden API request. You do not have the necessary permissions." } } + ], + "testPostSalesforceConnectDisconnectedUserSignup":[ + { + "description": "Should return the current user for valid code and redirect_uri while signup of disconnected user", + "input": { + "body": { + "code": "1234567", + "redirect_uri": "http://localhost:3000" + } + }, + "mocks": { + "getTokens": { + "access_token": "xyz", + "refresh_token": "xyz", + "signature": "xyz", + "scope": "xyz", + "id_token": "xyz", + "instance_url": "xyz", + "id": "https://test.salesforce.com/id/000Org-id/00112233445566AAA15", + "token_type": "Bearer", + "issued_at": "1690520247198" + }, + "getIdentity": { + "sub": "https://test.salesforce.com/id/000Org-id/00112233445566AAA15", + "user_id": "00112233445566AAA15", + "organization_id": "000Org-id", + "name": "Test User", + "email": "test@test.com" + } + }, + "output": { + "current_user": { + "id": "SALESFORCE-00112233445566AAA15", + "name": "Test User", + "email": "test@test.com" + } + } + } ] } \ No newline at end of file diff --git a/src/test/resources/data/controllers/authController/redirectUrl.scenarios.json b/src/test/resources/data/functional/controllers/authController/redirectUrl.scenarios.json similarity index 100% rename from src/test/resources/data/controllers/authController/redirectUrl.scenarios.json rename to src/test/resources/data/functional/controllers/authController/redirectUrl.scenarios.json diff --git a/src/test/resources/data/functional/controllers/crmOrganizationUserController/getCrmOrganizationUserList.scenarios.json b/src/test/resources/data/functional/controllers/crmOrganizationUserController/getCrmOrganizationUserList.scenarios.json new file mode 100644 index 00000000..b94d553a --- /dev/null +++ b/src/test/resources/data/functional/controllers/crmOrganizationUserController/getCrmOrganizationUserList.scenarios.json @@ -0,0 +1,128 @@ +[ + { + "description": "Should return the list of crm Users", + "input": { + "q": "a" + }, + "mocks": { + "makeCompositeRequest": { + "compositeResponse": [ + { + "body": { + "totalSize": 2, + "done": true, + "records": [ + { + "attributes": { + "type": "User", + "url": "/services/data/v58.0/sobjects/User/0051e0000047G03AAE" + }, + "Id": "0051e0000047G03AAE", + "Name": "Alpesh Modi" + }, + { + "attributes": { + "type": "User", + "url": "/services/data/v58.0/sobjects/User/0051e000004NQ6fAAG" + }, + "Id": "0051e000004NQ6fAAG", + "Name": "Aman Barbaria" + } + ] + }, + "httpHeaders": {}, + "httpStatusCode": 200, + "referenceId": "getAccounts" + } + ] + } + }, + "output": { + "crm_organization_user_ids": [ + "0051e0000047G03AAE", + "0051e000004NQ6fAAG" + ], + "crm_organization_user_map_by_id": { + "0051e0000047G03AAE": { + "id": "0051e0000047G03AAE", + "name": "Alpesh Modi" + }, + "0051e000004NQ6fAAG": { + "id": "0051e000004NQ6fAAG", + "name": "Aman Barbaria" + } + } + } + }, + { + "description": "Should return the list of crm users sorted by modified date on empty search string", + "input": { + "q": "" + }, + "mocks": { + "makeCompositeRequest": { + "compositeResponse": [ + { + "body": { + "totalSize": 2, + "done": true, + "records": [ + { + "attributes": { + "type": "User", + "url": "/services/data/v58.0/sobjects/User/0051e0000047G03AAE" + }, + "Id": "0051e0000047G03AAE", + "Name": "Alpesh Modi" + }, + { + "attributes": { + "type": "User", + "url": "/services/data/v58.0/sobjects/User/0051e000004NQ6fAAG" + }, + "Id": "0051e000004NQ6fAAG", + "Name": "Aman Barbaria" + } + ] + }, + "httpHeaders": {}, + "httpStatusCode": 200, + "referenceId": "getAccounts" + } + ] + } + }, + "output": { + "crm_organization_user_ids": [ + "0051e0000047G03AAE", + "0051e000004NQ6fAAG" + ], + "crm_organization_user_map_by_id": { + "0051e0000047G03AAE": { + "id": "0051e0000047G03AAE", + "name": "Alpesh Modi" + }, + "0051e000004NQ6fAAG": { + "id": "0051e000004NQ6fAAG", + "name": "Aman Barbaria" + } + } + } + }, + { + "description": "Should return error if search string length > 200", + "input": { + "q": "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz" + }, + "mocks": {}, + "output": { + "http_code": 400, + "message": "At least one parameter is invalid or missing.", + "code": "INVALID_PARAMS", + "internal_error_identifier": "b_2", + "param_errors": [ + "search_term_too_long" + ] + } + } + ] \ No newline at end of file diff --git a/src/test/resources/data/functional/controllers/suggestionsController/crmActionsSuggestions.scenarios.json b/src/test/resources/data/functional/controllers/suggestionsController/crmActionsSuggestions.scenarios.json new file mode 100644 index 00000000..740e1765 --- /dev/null +++ b/src/test/resources/data/functional/controllers/suggestionsController/crmActionsSuggestions.scenarios.json @@ -0,0 +1,160 @@ +[ + { + "description": "Should return correct suggestions for a given text", + "input": { + "text": "We reviewed the tasks that the team has completed since the last meeting and discussed additional projects.\n\nHere are the main topics we discussed:\n\nJoe updated us on the calendar for editorial, advertorial and social media content.\n" + }, + "mocks":{ + "makeRequest":{ + "id": "chatcmpl-0000", + "object": "chat.completion", + "created": 1692945329, + "model": "gpt-3.5-turbo-0613", + "choices": [ + { + "index": 0, + "message": { + "role": "assistant", + "content": null, + "function_call": { + "name": "suggest_actions", + "arguments": "{\n \"add_task\": [\n {\n \"description\": \"Review calendar for editorial, advertorial, and social media content\",\n \"due_date\": \"2023-08-30\"\n }\n ]\n}" + } + }, + "finish_reason": "function_call" + } + ], + "usage": { + "prompt_tokens": 168, + "completion_tokens": 52, + "total_tokens": 220 + } + } + }, + "output": { + "add_task_suggestions": [ + { + "description": "Review calendar for editorial, advertorial, and social media content", + "due_date": "2023-08-30" + } + ] + } + }, + { + "description": "Should return correct response for 0 suggestions", + "input": { + "text": "Text" + }, + "mocks":{ + "makeRequest":{ + "id": "chatcmpl-0000", + "object": "chat.completion", + "created": 1692945329, + "model": "gpt-3.5-turbo-0613", + "choices": [ + { + "index": 0, + "message": { + "role": "assistant", + "content": null, + "function_call": { + "name": "suggest_actions", + "arguments": "{\n \"add_task\": []\n}" + } + }, + "finish_reason": "function_call" + } + ], + "usage": { + "prompt_tokens": 168, + "completion_tokens": 52, + "total_tokens": 220 + } + } + }, + "output": { + "add_task_suggestions": [] + } + }, + { + "description": "Should remove due_date from the response if it is not in correct format", + "input": { + "text": "We reviewed the tasks that the team has completed since the last meeting and discussed additional projects.\n\nHere are the main topics we discussed:\n\nJoe updated us on the calendar for editorial, advertorial and social media content.\n" + }, + "mocks":{ + "makeRequest":{ + "id": "chatcmpl-0000", + "object": "chat.completion", + "created": 1692945329, + "model": "gpt-3.5-turbo-0613", + "choices": [ + { + "index": 0, + "message": { + "role": "assistant", + "content": null, + "function_call": { + "name": "suggest_actions", + "arguments": "{\n \"add_task\": [\n {\n \"description\": \"Review calendar for editorial, advertorial, and social media content\",\n \"due_date\": \"next week\"\n }\n ]\n}" + } + }, + "finish_reason": "function_call" + } + ], + "usage": { + "prompt_tokens": 168, + "completion_tokens": 52, + "total_tokens": 220 + } + } + }, + "output": { + "add_task_suggestions": [ + { + "description": "Review calendar for editorial, advertorial, and social media content", + "due_date": null + } + ] + } + }, + { + "description": "Should return error if no text is not provided", + "input": {}, + "mocks": {}, + "output": { + "http_code": 400, + "code": "INVALID_PARAMS", + "param_errors": [ + "missing_text" + ] + } + }, + { + "description": "Should return error if text length is greater than 12000", + "input": { + "text": "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum. Nam quam nunc, blandit vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec odio et ante tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. Nullam quis ante. Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed consequat, leo eget bibendum sodales, augue velit cursus nunc, quis gravida magna mi a libero. Fusce vulputate eleifend sapien. Vestibulum purus quam, scelerisque ut, mollis sed, nonummy id, metus. Nullam accumsan lorem in dui. Cras ultricies mi eu turpis hendrerit fringilla. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; In ac dui quis mi consectetuer lacinia. Nam pretium turpis et arcu. Duis arcu tortor, suscipit eget, imperdiet nec, imperdiet iaculis, ipsum. Sed aliquam ultrices mauris. Integer ante arcu, accumsan a, consectetuer eget, posuere ut, mauris. Praesent adipiscing. Phasellus ullamcorper ipsum rutrum nunc. Nunc nonummy metus. Vestibulum volutpat pretium libero. Cras id dui. Aenean ut eros et nisl sagittis vestibulum. Nullam nulla eros, ultricies sit amet, nonummy id, imperdiet feugiat, pede. Sed lectus. Donec mollis hendrerit risus. Phasellus nec sem in justo pellentesque facilisis. Etiam imperdiet imperdiet orci. Nunc nec neque. Phasellus leo dolor, tempus non, auctor et, hendrerit quis, nisi. Curabitur ligula sapien, tincidunt non, euismod vitae, posuere imperdiet, leo. Maecenas malesuada. Praesent congue erat at massa. Sed cursus turpis vitae tortor. Donec posuere vulputate arcu. Phasellus accumsan cursus velit. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed aliquam, nisi quis porttitor congue, elit erat euismod orci, ac placerat dolor lectus quis orci. Phasellus consectetuer vestibulum elit. Aenean tellus metus, bibendum sed, posuere ac, mattis non, nunc. Vestibulum fringilla pede sit amet augue. In turpis. Pellentesque posuere. Praesent turpis. Aenean posuere, tortor sed cursus feugiat, nunc augue blandit nunc, eu sollicitudin urna dolor sagittis lacus. Donec elit libero, sodales nec, volutpat a, suscipit non, turpis. Nullam sagittis. Suspendisse pulvinar, augue ac venenatis condimentum, sem libero volutpat nibh, nec pellentesque velit pede quis nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Fusce id purus. Ut varius tincidunt libero. Phasellus dolor. Maecenas vestibulum mollis diam. Pellentesque ut neque. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. In dui magna, posuere eget, vestibulum et, tempor auctor, justo. In ac felis quis tortor malesuada pretium. Pellentesque auctor neque nec urna. Proin sapien ipsum, porta a, auctor quis, euismod ut, mi. Aenean viverra rhoncus pede. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Ut non enim eleifend felis pretium feugiat. Vivamus quis mi. Phasellus a est. Phasellus magna. In hac habitasse platea dictumst. Curabitur at lacus ac velit ornare lobortis. Curabitur a felis in nunc fringilla tristique. Morbi mattis ullamcorper velit. Phasellus gravida semper nisi. Nullam vel sem. Pellentesque libero tortor, tincidunt et, tincidunt eget, semper nec, quam. Sed hendrerit. Morbi ac felis. Nunc egestas, augue at pellentesque laoreet, felis eros vehicula leo, at malesuada velit leo quis pede. Donec interdum, metus et hendrerit aliquet, dolor diam sagittis ligula, eget egestas libero turpis vel mi. Nunc nulla. Fusce risus nisl, viverra et, tempor et, pretium in, sapien. Donec venenatis vulputate lorem. Morbi nec metus. Phasellus blandit leo ut odio. Maecenas ullamcorper, dui et placerat feugiat, eros pede varius nisi, condimentum viverra felis nunc et lorem. Sed magna purus, fermentum eu, tincidunt eu, varius ut, felis. In auctor lobortis lacus. Quisque libero metus, condimentum nec, tempor a, commodo mollis, magna. Vestibulum ullamcorper mauris at ligula. Fusce fermentum. Nullam cursus lacinia erat. Praesent blandit laoreet nibh. Fusce convallis metus id felis luctus adipiscing. Pellentesque egestas, neque sit amet convallis pulvinar, justo nulla eleifend augue, ac auctor orci leo non est. Quisque id mi. Ut tincidunt tincidunt erat. Etiam feugiat lorem non metus. Vestibulum dapibus nunc ac augue. Curabitur vestibulum aliquam leo. Praesent egestas neque eu enim. In hac habitasse platea dictumst. Fusce a quam. Etiam ut purus mattis mauris sodales aliquam. Curabitur nisi. Quisque malesuada placerat nisl. Nam ipsum risus, rutrum vitae, vestibulum eu, molestie vel, lacus. Sed augue ipsum, egestas nec, vestibulum et, malesuada adipiscing, dui. Vestibulum facilisis, purus nec pulvinar iaculis, ligula mi congue nunc, vitae euismod ligula urna in dolor. Mauris sollicitudin fermentum libero. Praesent nonummy mi in odio. Nunc interdum lacus sit amet orci. Vestibulum rutrum, mi nec elementum vehicula, eros quam gravida nisl, id fringilla neque ante vel mi. Morbi mollis tellus ac sapien. Phasellus volutpat, metus eget egestas mollis, lacus lacus blandit dui, id egestas quam mauris ut lacus. Fusce vel dui. Sed in libero ut nibh placerat accumsan. Proin faucibus arcu quis ante. In consectetuer turpis ut velit. Nulla sit amet est. Praesent metus tellus, elementum eu, semper a, adipiscing nec, purus. Cras risus ipsum, faucibus ut, ullamcorper id, varius ac, leo. Suspendisse feugiat. Suspendisse enim turpis, dictum sed, iaculis a, condimentum nec, nisi. Praesent nec nisl a purus blandit viverra. Praesent ac massa at ligula laoreet iaculis. Nulla neque dolor, sagittis eget, iaculis quis, molestie non, velit. Mauris turpis nunc, blandit et, volutpat molestie, porta ut, ligula. Fusce pharetra convallis urna. Quisque ut nisi. Donec mi odio, faucibus at, scelerisque quis, convallis in, nisi. Suspendisse non nisl sit amet velit hendrerit rutrum. Ut leo. Ut a nisl id ante tempus hendrerit. Proin pretium, leo ac pellentesque mollis, felis nunc ultrices eros, sed gravida augue augue mollis justo. Suspendisse eu ligula. Nulla facilisi. Donec id justo. Praesent porttitor, nulla vitae posuere iaculis, arcu nisl dignissim dolor, a pretium mi sem ut ipsum. Curabitur suscipit suscipit tellus. Praesent vestibulum dapibus nibh. Etiam iaculis nunc ac metus. Ut id nisl quis enim dignissim sagittis. Etiam sollicitudin, ipsum eu pulvinar rutrum, tellus ipsum laoreet sapien, quis venenatis ante odio sit amet eros. Proin magna. Duis vel nibh at velit scelerisque suscipit. Curabitur turpis. Vestibulum suscipit nulla quis orci. Fusce ac felis sit amet ligula pharetra condimentum. Maecenas egestas arcu quis ligula mattis placerat. Duis lobortis massa imperdiet quam. Suspendisse potenti. Pellentesque commodo eros a enim. Vestibulum turpis sem, aliquet eget, lobortis pellentesque, rutrum eu, nisl. Sed libero. Aliquam erat volutpat. Etiam vitae tortor. Morbi vestibulum volutpat enim. Aliquam eu nunc. Nunc sed turpis. Sed mollis, eros et ultrices tempus, mauris ipsum aliquam libero, non adipiscing dolor urna a orci. Nulla porta dolor. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Pellentesque dapibus hendrerit tortor. Praesent egestas tristique nibh. Sed a libero. Cras varius. Donec vitae orci sed dolor rutrum auctor. Fusce egestas elit eget lorem. Suspendisse nisl elit, rhoncus eget, elementum ac, condimentum eget, diam. Nam at tortor in tellus interdum sagittis. Aliquam lobortis. Donec orci lectus, aliquam ut, faucibus non, euismod id, nulla. Curabitur blandit mollis lacus. Nam adipiscing. Vestibulum eu odio. Vivamus laoreet. Nullam tincidunt adipiscing enim. Phasellus tempus. Proin viverra, ligula sit amet ultrices semper, ligula arcu tristique sapien, a accumsan nisi mauris ac eros. Fusce neque. Suspendisse faucibus, nunc et pellentesque egestas, lacus ante convallis tellus, vitae iaculis lacus elit id tortor. Vivamus aliquet elit ac nisl. Fusce fermentum odio nec arcu. Vivamus euismod mauris. In ut quam vitae odio lacinia tincidunt. Praesent ut ligula non mi varius sagittis. Cras sagittis. Praesent ac sem eget est egestas volutpat. Vivamus consectetuer hendrerit lacus. Cras non dolor. Vivamus in erat ut urna cursus vestibulum. Fusce commodo aliquam arcu. Nam commodo suscipit quam. Quisque id odio. Praesent venenatis metus at tortor pulvinar varius. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum. Nam quam nunc, blandit vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec odio et ante tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. Nullam quis ante. Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed consequat, leo eget bibendum sodales, augue velit cursus nunc, quis gravida magna mi a libero. Fusce vulputate eleifend sapien. Vestibulum purus quam, scelerisque ut, mollis sed, nonummy id, metus. Nullam accumsan lorem in dui. Cras ultricies mi eu turpis hendrerit fringilla. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; In ac dui quis mi consectetuer lacinia. Nam pretium turpis et arcu. Duis arcu tortor, suscipit eget, imperdiet nec, imperdiet iaculis, ipsum. Sed aliquam ultrices mauris. Integer ante arcu, accumsan a, consectetuer eget, posuere ut, mauris. Praesent adipiscing. Phasellus ullamcorper ipsum rutrum nunc. Nunc nonummy metus. Vestibulum volutpat pretium libero. Cras id dui. Aenean ut eros et nisl sagittis vestibulum. Nullam nulla eros, ultricies sit amet, nonummy id, imperdiet feugiat, pede. Sed lectus. Donec mollis hendrerit risus. Phasellus nec sem in justo pellentesque facilisis. Etiam imperdiet imperdiet orci. Nunc nec neque. Phasellus leo dolor, tempus non, auctor et, hendrerit quis, nisi. Curabitur ligula sapien, tincidunt non, euismod vitae, posuere imperdiet, leo. Maecenas malesuada. Praesent congue erat at massa. Sed cursus turpis vitae tortor. Donec posuere vulputate arcu. Phasellus accumsan cursus velit. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed aliquam, nisi quis porttitor" + }, + "mocks": {}, + "output": { + "http_code": 400, + "code": "INVALID_PARAMS", + "param_errors": [ + "text_too_long" + ] + } + }, + { + "description": "Should return error if text is empty", + "input": { + "text": "" + }, + "mocks": {}, + "output": { + "http_code": 400, + "code": "INVALID_PARAMS", + "param_errors": [ + "missing_text" + ] + } + } +] \ No newline at end of file diff --git a/src/test/resources/data/controllers/userController/getCurrentUser.scenarios.json b/src/test/resources/data/functional/controllers/userController/getCurrentUser.scenarios.json similarity index 62% rename from src/test/resources/data/controllers/userController/getCurrentUser.scenarios.json rename to src/test/resources/data/functional/controllers/userController/getCurrentUser.scenarios.json index 259a5ef9..16920d99 100644 --- a/src/test/resources/data/controllers/userController/getCurrentUser.scenarios.json +++ b/src/test/resources/data/functional/controllers/userController/getCurrentUser.scenarios.json @@ -2,11 +2,11 @@ { "description": "Should return current user", "input": { - "cookie": "1:00112233445566AAA1:SALESFORCE:1899000000:IbFOqqub193hajGqMDwrF41iUiKl8KJKWpmPl7oqX4eesrydP9cSx483f86OFK+dtSNAukoy+Tr7pRYsD3yVNw==" + "cookie": "1:0055i00000AUxQHAA1:SALESFORCE:app:1692878227:a2xOxjcPChK/YsKCWRuY50J3gH21/tqcGlv7sWU/aPw3/UFVFPbvWn2LLzY5B5kVPXjj1gELRqa4B4ds6uuqIw==" }, "output": { "current_user": { - "id": "00112233445566AAA1", + "id": "0055i00000AUxQHAA1", "name": "Test User", "email": "test@test.com" } @@ -15,7 +15,7 @@ { "description": "Should return error if invalid cookie", "input": { - "cookie": "1:00112233445566AAA1:SALESFORCE:1899000000:invalid" + "cookie": "1:0055i00000AUxQHAA1:SALESFORCE:app:1899000000:invalid" }, "output": { "http_code": 401, @@ -33,7 +33,7 @@ { "description": "Should return error if expired cookie", "input": { - "cookie": "1:00112233445566AAA1:SALESFORCE:1592189876:IbFOqqub193hajGqMDwrF41iUiKl8KJKWpmPl7oqX4eesrydP9cSx483f86OFK+dtSNAukoy+Tr7pRYsD3yVNw==" + "cookie": "1:0055i00000AUxQHAA1:SALESFORCE:app:1592189876:IbFOqqub193hajGqMDwrF41iUiKl8KJKWpmPl7oqX4eesrydP9cSx483f86OFK+dtSNAukoy+Tr7pRYsD3yVNw==" }, "output": { "http_code": 401, diff --git a/src/test/resources/data/functional/exceptions/globalExceptionHandler.scenarios.json b/src/test/resources/data/functional/exceptions/globalExceptionHandler.scenarios.json new file mode 100644 index 00000000..02f696f3 --- /dev/null +++ b/src/test/resources/data/functional/exceptions/globalExceptionHandler.scenarios.json @@ -0,0 +1,13 @@ +[ + { + "description": "Should return 404 error", + "input": {}, + "output": { + "http_code":404, + "message":"Resource not found. Please check if the resource identifier passed in the URL is valid and try again.", + "code":"NOT_FOUND", + "internal_error_identifier":"a_e_geh_nf_1", + "param_errors":[] + } + } +] \ No newline at end of file diff --git a/src/test/resources/data/functional/interceptors/jsonOnlyInterceptor.scenarios.json b/src/test/resources/data/functional/interceptors/jsonOnlyInterceptor.scenarios.json new file mode 100644 index 00000000..0dbd5bc1 --- /dev/null +++ b/src/test/resources/data/functional/interceptors/jsonOnlyInterceptor.scenarios.json @@ -0,0 +1,13 @@ +[ + { + "description": "Should return 415 content type error", + "input": {}, + "output": { + "http_code":415, + "message":"Unsupported Media Type. Content-Type header should be application/json.", + "code":"UNSUPPORTED_MEDIA_TYPE", + "internal_error_identifier":"i_joi_ph_1", + "param_errors":[] + } + } +] \ No newline at end of file diff --git a/src/test/resources/data/unit/lib/openAi/openAiRequest.scenarios.json b/src/test/resources/data/unit/lib/openAi/openAiRequest.scenarios.json new file mode 100644 index 00000000..42a9b43b --- /dev/null +++ b/src/test/resources/data/unit/lib/openAi/openAiRequest.scenarios.json @@ -0,0 +1,68 @@ +[ + { + "description": "Should return correct result", + "input": { + "payload": { + "model": "model", + "messages": [ + { + "role": "user", + "content": "hello" + } + ] + } + }, + "mocks":{ + "makeRequest":{ + "id": "chatcmpl-0000", + "object": "chat.completion", + "created": 1692945329, + "model": "gpt-3.5-turbo-0613", + "choices": [ + { + "index": 0, + "message": { + "role": "assistant", + "content": null, + "function_call": { + "name": "suggest_actions", + "arguments": "{\n \"add_task\": [\n {\n \"description\": \"Review calendar for editorial, advertorial, and social media content\",\n \"due_date\": \"2023-08-30\"\n }\n ]\n}" + } + }, + "finish_reason": "function_call" + } + ], + "usage": { + "prompt_tokens": 168, + "completion_tokens": 52, + "total_tokens": 220 + } + } + }, + "output": { + "id": "chatcmpl-0000", + "object": "chat.completion", + "created": 1692945329, + "model": "gpt-3.5-turbo-0613", + "choices": [ + { + "index": 0, + "message": { + "role": "assistant", + "content": null, + "function_call": { + "name": "suggest_actions", + "arguments": "{\n \"add_task\": [\n {\n \"description\": \"Review calendar for editorial, advertorial, and social media content\",\n \"due_date\": \"2023-08-30\"\n }\n ]\n}" + } + }, + "finish_reason": "function_call" + } + ], + "usage": { + "prompt_tokens": 168, + "completion_tokens": 52, + "total_tokens": 220 + } + } + } +] \ No newline at end of file diff --git a/src/test/resources/data/unit/lib/openAi/openAiRequestError.scenarios.json b/src/test/resources/data/unit/lib/openAi/openAiRequestError.scenarios.json new file mode 100644 index 00000000..04c48b25 --- /dev/null +++ b/src/test/resources/data/unit/lib/openAi/openAiRequestError.scenarios.json @@ -0,0 +1,56 @@ +[ + { + "description": "Test 401 response", + "input": { + "payload": { + "model": "model", + "messages": [ + { + "role": "user", + "content": "hello" + } + ] + }, + "statusCode": 401 + }, + "output": { + "apiErrorIdentifier": "something_went_wrong" + } + }, + { + "description": "Test 400 response", + "input": { + "payload": { + "model": "model", + "messages": [ + { + "role": "user", + "content": "hello" + } + ] + }, + "statusCode": 400 + }, + "output": { + "apiErrorIdentifier": "something_went_wrong" + } + }, + { + "description": "Test 500 response", + "input": { + "payload": { + "model": "model", + "messages": [ + { + "role": "user", + "content": "hello" + } + ] + }, + "statusCode": 500 + }, + "output": { + "apiErrorIdentifier": "something_went_wrong" + } + } +] \ No newline at end of file diff --git a/src/test/resources/data/unit/repositories/salesforceOauthTokenRepository.scenarios.json b/src/test/resources/data/unit/repositories/salesforceOauthTokenRepository.scenarios.json new file mode 100644 index 00000000..a755c994 --- /dev/null +++ b/src/test/resources/data/unit/repositories/salesforceOauthTokenRepository.scenarios.json @@ -0,0 +1,41 @@ +{ + "testInsert": [ + { + "description": "Insert New Item", + "input": { + "accessToken": "sampleAccessToken", + "refreshToken": "sampleRefreshToken", + "signature": "as/FLavAQ8U=", + "instanceUrl": "https://truesparrowsystemspvtltd--dev.sandbox.my.salesforce.com", + "idToken": "eyJraWQiOiI", + "identityUrl": "https://test.salesforce.com/id/00D1e0000000NRYEA2/0055i00000AUxQHAA1", + "issuedAt": "1692265987212", + "externalUserId": "0055i00000AUxQHAA1", + "status": "ACTIVE" + }, + "output": {} + } + ], + "testUpdate": [ + { + "description": "Update Existing Item", + "input": { + "externalUserId": "0055i00000AUxQHAA1", + "accessToken": "updatedAccessToken", + "refreshToken": "updatedRefreshToken" + }, + "output": {} + } + ], + "testUpdateWithNullAttributes": [ + { + "description": "Skip Null Attributes During Update", + "input": { + "externalUserId": "0055i00000AUxQHAA1", + "accessToken": "newAccessToken", + "idToken": null + }, + "output": {} + } + ] +} \ No newline at end of file diff --git a/src/test/resources/fixtures/common/repositories/salesforceOauthToken/ActiveSalesforceOauthToken.json b/src/test/resources/fixtures/common/repositories/salesforceOauthToken/ActiveSalesforceOauthToken.json new file mode 100644 index 00000000..1aa992c5 --- /dev/null +++ b/src/test/resources/fixtures/common/repositories/salesforceOauthToken/ActiveSalesforceOauthToken.json @@ -0,0 +1,13 @@ +{ + "accessToken": "AQICAHh8THF/Dummy=", + "refreshToken":"T2Fybjphd3M6a21zOid1cy1lYXN0LTEnOicxMTExMjIyMjMzMzMnOmtleS9iYzQzNjQ4NS01MDkyLTQyYjgtOTJhMy0wYWE4YjkzNTM2ZGMAAAAAh3q5+k3TUhmtkD6iwMI5nBJf6PZbNXb44fj0amxIBWDvK4Gd/M38cMpwG4Mg", + "updatedAt": "2023-08-17T09:53:08.331Z", + "signature": "FCGHH2fMCTciwY6zwcrBin10xO8hYwp/FLavAQ8U=", + "instanceUrl": "https://salesforce.com", + "idToken": "dummy--ee", + "createdAt": "2023-08-17T09:53:08.331Z", + "identityUrl": "https://salesforce.com/id/000Org-id/0055i00000AUxQHAA1", + "issuedAt": "1692265987212", + "externalUserId": "0055i00000AUxQHAA1", + "status": "ACTIVE" +} \ No newline at end of file diff --git a/src/test/resources/fixtures/common/repositories/salesforceOrganization/ActiveSalesforceOrganization.json b/src/test/resources/fixtures/common/repositories/salesforceOrganization/ActiveSalesforceOrganization.json new file mode 100644 index 00000000..fbb2e794 --- /dev/null +++ b/src/test/resources/fixtures/common/repositories/salesforceOrganization/ActiveSalesforceOrganization.json @@ -0,0 +1,4 @@ +{ + "externalOrganizationId": "000Org-id", + "status": "ACTIVE" +} \ No newline at end of file diff --git a/src/test/resources/fixtures/repositories/salesforceOrganization/DeletedSalesforceOrganization.json b/src/test/resources/fixtures/common/repositories/salesforceOrganization/DeletedSalesforceOrganization.json similarity index 100% rename from src/test/resources/fixtures/repositories/salesforceOrganization/DeletedSalesforceOrganization.json rename to src/test/resources/fixtures/common/repositories/salesforceOrganization/DeletedSalesforceOrganization.json diff --git a/src/test/resources/fixtures/common/repositories/salesforceUser/ActiveSalesforceUser.json b/src/test/resources/fixtures/common/repositories/salesforceUser/ActiveSalesforceUser.json new file mode 100644 index 00000000..f87411dd --- /dev/null +++ b/src/test/resources/fixtures/common/repositories/salesforceUser/ActiveSalesforceUser.json @@ -0,0 +1,11 @@ +{ + "externalUserId": "0055i00000AUxQHAA1", + "identityUrl": "https://test.salesforce.com/id/000Org-id/0055i00000AUxQHAA1", + "externalOrganizationId": "000Org-id", + "name": "Test User", + "email": "test@test.com", + "userKind": "SALESFORCE", + "cookieToken": "oGzc1IRNmu45Oe73KBYipqDSsgC4n5vepVt3P9+jJiX0wr39SVb0J8ontP277D3NdkHnyL0HPDFuwTSQxNz2gSUHaTMfe0oebcxl9AMUpIw=", + "encryptionSalt": "Qc8x8AZvWZBqnYJ9Xk6+CbyHmHi29DvL8vzwFZULsquHGICDip9rPMpzG+LVtUOh", + "status": "ACTIVE" +} diff --git a/src/test/resources/fixtures/repositories/salesforceUser/ActiveSalesforceUserForLogin.json b/src/test/resources/fixtures/common/repositories/salesforceUser/ActiveSalesforceUserForLogin.json similarity index 100% rename from src/test/resources/fixtures/repositories/salesforceUser/ActiveSalesforceUserForLogin.json rename to src/test/resources/fixtures/common/repositories/salesforceUser/ActiveSalesforceUserForLogin.json diff --git a/src/test/resources/fixtures/common/repositories/salesforceUser/DeletedSalesforceUser.json b/src/test/resources/fixtures/common/repositories/salesforceUser/DeletedSalesforceUser.json new file mode 100644 index 00000000..6c9b1d39 --- /dev/null +++ b/src/test/resources/fixtures/common/repositories/salesforceUser/DeletedSalesforceUser.json @@ -0,0 +1,4 @@ +{ + "externalUserId": "00112233445566AAA15", + "status": "DELETED" +} \ No newline at end of file diff --git a/src/test/resources/fixtures/controllers/accountController/createNote.fixtures.json b/src/test/resources/fixtures/controllers/accountController/createNote.fixtures.json deleted file mode 100644 index c62d64b4..00000000 --- a/src/test/resources/fixtures/controllers/accountController/createNote.fixtures.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "createNote": { - "salesforce_users": [ - { - "filepath": "classpath:fixtures/repositories/salesforceUser/ActiveSalesforceUser.json" - } - ], - "salesforce_oauth_tokens": [ - { - "filepath": "classpath:fixtures/repositories/salesforceOauthToken/ActiveSalesforceOauthToken.json" - } - ] - } -} \ No newline at end of file diff --git a/src/test/resources/fixtures/controllers/accountController/getAccountList.fixtures.json b/src/test/resources/fixtures/controllers/accountController/getAccountList.fixtures.json deleted file mode 100644 index 6444679c..00000000 --- a/src/test/resources/fixtures/controllers/accountController/getAccountList.fixtures.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "getAccountList": { - "salesforce_users": [ - { - "filepath": "classpath:fixtures/repositories/salesforceUser/ActiveSalesforceUser.json" - } - ], - "salesforce_oauth_tokens": [ - { - "filepath": "classpath:fixtures/repositories/salesforceOauthToken/ActiveSalesforceOauthToken.json" - } - ] - } -} \ No newline at end of file diff --git a/src/test/resources/fixtures/controllers/accountController/getAccountsFeed.fixtures.json b/src/test/resources/fixtures/controllers/accountController/getAccountsFeed.fixtures.json new file mode 100644 index 00000000..a2959bbe --- /dev/null +++ b/src/test/resources/fixtures/controllers/accountController/getAccountsFeed.fixtures.json @@ -0,0 +1,14 @@ +{ + "getAccountsFeed": { + "salesforce_users": [ + { + "filepath": "classpath:fixtures/common/repositories/salesforceUser/ActiveSalesforceUser.json" + } + ], + "salesforce_oauth_tokens": [ + { + "filepath": "classpath:fixtures/common/repositories/salesforceOauthToken/ActiveSalesforceOauthToken.json" + } + ] + } +} \ No newline at end of file diff --git a/src/test/resources/fixtures/controllers/accountController/getNoteDetails.fixtures.json b/src/test/resources/fixtures/controllers/accountController/getNoteDetails.fixtures.json deleted file mode 100644 index a8f387c8..00000000 --- a/src/test/resources/fixtures/controllers/accountController/getNoteDetails.fixtures.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "getNoteDetails": { - "salesforce_users": [ - { - "filepath": "classpath:fixtures/repositories/salesforceUser/ActiveSalesforceUser.json" - } - ], - "salesforce_oauth_tokens": [ - { - "filepath": "classpath:fixtures/repositories/salesforceOauthToken/ActiveSalesforceOAuthToken.json" - } - ] - } - } \ No newline at end of file diff --git a/src/test/resources/fixtures/controllers/accountController/getNotesList.fixtures.json b/src/test/resources/fixtures/controllers/accountController/getNotesList.fixtures.json deleted file mode 100644 index 18ff7504..00000000 --- a/src/test/resources/fixtures/controllers/accountController/getNotesList.fixtures.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "getNoteList": { - "salesforce_users": [ - { - "filepath": "classpath:fixtures/repositories/salesforceUser/ActiveSalesforceUser.json" - } - ], - "salesforce_oauth_tokens": [ - { - "filepath": "classpath:fixtures/repositories/salesforceOauthToken/ActiveSalesforceOAuthToken.json" - } - ] - } - } \ No newline at end of file diff --git a/src/test/resources/fixtures/controllers/authController/PostLogoutFixture.json b/src/test/resources/fixtures/controllers/authController/PostLogoutFixture.json deleted file mode 100644 index 7f47e4a1..00000000 --- a/src/test/resources/fixtures/controllers/authController/PostLogoutFixture.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "testPostLogout": { - "salesforce_users": [ - { - "filepath": "classpath:fixtures/repositories/salesforceUser/ActiveSalesforceUser.json" - } - ] - } -} \ No newline at end of file diff --git a/src/test/resources/fixtures/controllers/authController/PostSalesforceConnectFixture.json b/src/test/resources/fixtures/controllers/authController/PostSalesforceConnectFixture.json deleted file mode 100644 index cfaf9f5a..00000000 --- a/src/test/resources/fixtures/controllers/authController/PostSalesforceConnectFixture.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "testPostSalesforceConnectLogin": { - "salesforce_users": [ - { - "filepath": "classpath:fixtures/repositories/salesforceUser/ActiveSalesforceUser.json" - }, - { - "filepath": "classpath:fixtures/repositories/salesforceUser/ActiveSalesforceUserForLogin.json" - } - ], - "salesforce_oauth_tokens": [ - { - "filepath": "classpath:fixtures/repositories/salesforceOauthToken/ActiveSalesforceOauthToken.json" - } - ], - "salesforce_organizations": [ - { - "filepath": "classpath:fixtures/repositories/salesforceOrganization/ActiveSalesforceOrganization.json" - }, - { - "filepath": "classpath:fixtures/repositories/salesforceOrganization/DeletedSalesforceOrganization.json" - } - ] - } -} \ No newline at end of file diff --git a/src/test/resources/fixtures/controllers/userController/getCurrentUser.fixtures.json b/src/test/resources/fixtures/controllers/userController/getCurrentUser.fixtures.json deleted file mode 100644 index 60fa1ab8..00000000 --- a/src/test/resources/fixtures/controllers/userController/getCurrentUser.fixtures.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "getCurrentUser": { - "salesforce_users": [ - { - "filepath": "classpath:fixtures/repositories/salesforceUser/ActiveSalesforceUser.json" - } - ] - } -} \ No newline at end of file diff --git a/src/test/resources/fixtures/functional/controllers/accountController/createNote.fixtures.json b/src/test/resources/fixtures/functional/controllers/accountController/createNote.fixtures.json new file mode 100644 index 00000000..6741c9a3 --- /dev/null +++ b/src/test/resources/fixtures/functional/controllers/accountController/createNote.fixtures.json @@ -0,0 +1,14 @@ +{ + "createNote": { + "salesforce_users": [ + { + "filepath": "classpath:fixtures/common/repositories/salesforceUser/ActiveSalesforceUser.json" + } + ], + "salesforce_oauth_tokens": [ + { + "filepath": "classpath:fixtures/common/repositories/salesforceOauthToken/ActiveSalesforceOauthToken.json" + } + ] + } +} \ No newline at end of file diff --git a/src/test/resources/fixtures/functional/controllers/accountController/getAccountList.fixtures.json b/src/test/resources/fixtures/functional/controllers/accountController/getAccountList.fixtures.json new file mode 100644 index 00000000..a27c41cf --- /dev/null +++ b/src/test/resources/fixtures/functional/controllers/accountController/getAccountList.fixtures.json @@ -0,0 +1,14 @@ +{ + "getAccountList": { + "salesforce_users": [ + { + "filepath": "classpath:fixtures/common/repositories/salesforceUser/ActiveSalesforceUser.json" + } + ], + "salesforce_oauth_tokens": [ + { + "filepath": "classpath:fixtures/common/repositories/salesforceOauthToken/ActiveSalesforceOauthToken.json" + } + ] + } +} \ No newline at end of file diff --git a/src/test/resources/fixtures/functional/controllers/accountController/getNoteDetails.fixtures.json b/src/test/resources/fixtures/functional/controllers/accountController/getNoteDetails.fixtures.json new file mode 100644 index 00000000..f089a408 --- /dev/null +++ b/src/test/resources/fixtures/functional/controllers/accountController/getNoteDetails.fixtures.json @@ -0,0 +1,14 @@ +{ + "getNoteDetails": { + "salesforce_users": [ + { + "filepath": "classpath:fixtures/common/repositories/salesforceUser/ActiveSalesforceUser.json" + } + ], + "salesforce_oauth_tokens": [ + { + "filepath": "classpath:fixtures/common/repositories/salesforceOauthToken/ActiveSalesforceOauthToken.json" + } + ] + } +} \ No newline at end of file diff --git a/src/test/resources/fixtures/functional/controllers/accountController/getNotesList.fixtures.json b/src/test/resources/fixtures/functional/controllers/accountController/getNotesList.fixtures.json new file mode 100644 index 00000000..8118f7d9 --- /dev/null +++ b/src/test/resources/fixtures/functional/controllers/accountController/getNotesList.fixtures.json @@ -0,0 +1,14 @@ +{ + "getNoteList": { + "salesforce_users": [ + { + "filepath": "classpath:fixtures/common/repositories/salesforceUser/ActiveSalesforceUser.json" + } + ], + "salesforce_oauth_tokens": [ + { + "filepath": "classpath:fixtures/common/repositories/salesforceOauthToken/ActiveSalesforceOauthToken.json" + } + ] + } +} \ No newline at end of file diff --git a/src/test/resources/fixtures/functional/controllers/accountNoteController/deleteAccountNote.fixtures.json b/src/test/resources/fixtures/functional/controllers/accountNoteController/deleteAccountNote.fixtures.json new file mode 100644 index 00000000..10c6c018 --- /dev/null +++ b/src/test/resources/fixtures/functional/controllers/accountNoteController/deleteAccountNote.fixtures.json @@ -0,0 +1,14 @@ +{ + "deleteAccountNote": { + "salesforce_users": [ + { + "filepath": "classpath:fixtures/common/repositories/salesforceUser/ActiveSalesforceUser.json" + } + ], + "salesforce_oauth_tokens": [ + { + "filepath": "classpath:fixtures/common/repositories/salesforceOauthToken/ActiveSalesforceOauthToken.json" + } + ] + } +} \ No newline at end of file diff --git a/src/test/resources/fixtures/functional/controllers/accountTaskController/createTask.fixtures.json b/src/test/resources/fixtures/functional/controllers/accountTaskController/createTask.fixtures.json new file mode 100644 index 00000000..acfc8044 --- /dev/null +++ b/src/test/resources/fixtures/functional/controllers/accountTaskController/createTask.fixtures.json @@ -0,0 +1,14 @@ +{ + "createTask": { + "salesforce_users": [ + { + "filepath": "classpath:fixtures/common/repositories/salesforceUser/ActiveSalesforceUser.json" + } + ], + "salesforce_oauth_tokens": [ + { + "filepath": "classpath:fixtures/common/repositories/salesforceOauthToken/ActiveSalesforceOauthToken.json" + } + ] + } + } \ No newline at end of file diff --git a/src/test/resources/fixtures/functional/controllers/accountTaskController/deleteAccountTask.fixtures.json b/src/test/resources/fixtures/functional/controllers/accountTaskController/deleteAccountTask.fixtures.json new file mode 100644 index 00000000..f45e3a67 --- /dev/null +++ b/src/test/resources/fixtures/functional/controllers/accountTaskController/deleteAccountTask.fixtures.json @@ -0,0 +1,14 @@ +{ + "deleteAccountTask": { + "salesforce_users": [ + { + "filepath": "classpath:fixtures/common/repositories/salesforceUser/ActiveSalesforceUser.json" + } + ], + "salesforce_oauth_tokens": [ + { + "filepath": "classpath:fixtures/common/repositories/salesforceOauthToken/ActiveSalesforceOauthToken.json" + } + ] + } + } \ No newline at end of file diff --git a/src/test/resources/fixtures/functional/controllers/accountTaskController/getAccountTasksList.fixtures.json b/src/test/resources/fixtures/functional/controllers/accountTaskController/getAccountTasksList.fixtures.json new file mode 100644 index 00000000..7e9f2c8d --- /dev/null +++ b/src/test/resources/fixtures/functional/controllers/accountTaskController/getAccountTasksList.fixtures.json @@ -0,0 +1,14 @@ +{ + "getAccountTasksList": { + "salesforce_users": [ + { + "filepath": "classpath:fixtures/common/repositories/salesforceUser/ActiveSalesforceUser.json" + } + ], + "salesforce_oauth_tokens": [ + { + "filepath": "classpath:fixtures/common/repositories/salesforceOauthToken/ActiveSalesforceOauthToken.json" + } + ] + } + } \ No newline at end of file diff --git a/src/test/resources/fixtures/functional/controllers/authController/PostDisconnectFixture.json b/src/test/resources/fixtures/functional/controllers/authController/PostDisconnectFixture.json new file mode 100644 index 00000000..4cb807ce --- /dev/null +++ b/src/test/resources/fixtures/functional/controllers/authController/PostDisconnectFixture.json @@ -0,0 +1,21 @@ +{ + "testPostDisconnectSuccess": { + "salesforce_users": [ + { + "filepath": "classpath:fixtures/common/repositories/salesforceUser/ActiveSalesforceUser.json" + } + ], + "salesforce_oauth_tokens": [ + { + "filepath": "classpath:fixtures/common/repositories/salesforceOauthToken/ActiveSalesforceOauthToken.json" + } + ] + }, + "testPostDisconnectNoTokens": { + "salesforce_users": [ + { + "filepath": "classpath:fixtures/common/repositories/salesforceUser/ActiveSalesforceUser.json" + } + ] + } +} \ No newline at end of file diff --git a/src/test/resources/fixtures/functional/controllers/authController/PostLogoutFixture.json b/src/test/resources/fixtures/functional/controllers/authController/PostLogoutFixture.json new file mode 100644 index 00000000..7d870c5d --- /dev/null +++ b/src/test/resources/fixtures/functional/controllers/authController/PostLogoutFixture.json @@ -0,0 +1,9 @@ +{ + "testPostLogout": { + "salesforce_users": [ + { + "filepath": "classpath:fixtures/common/repositories/salesforceUser/ActiveSalesforceUser.json" + } + ] + } +} \ No newline at end of file diff --git a/src/test/resources/fixtures/functional/controllers/authController/PostSalesforceConnectFixture.json b/src/test/resources/fixtures/functional/controllers/authController/PostSalesforceConnectFixture.json new file mode 100644 index 00000000..e616d089 --- /dev/null +++ b/src/test/resources/fixtures/functional/controllers/authController/PostSalesforceConnectFixture.json @@ -0,0 +1,37 @@ +{ + "testPostSalesforceConnectLogin": { + "salesforce_users": [ + { + "filepath": "classpath:fixtures/common/repositories/salesforceUser/ActiveSalesforceUser.json" + }, + { + "filepath": "classpath:fixtures/common/repositories/salesforceUser/ActiveSalesforceUserForLogin.json" + } + ], + "salesforce_oauth_tokens": [ + { + "filepath": "classpath:fixtures/common/repositories/salesforceOauthToken/ActiveSalesforceOauthToken.json" + } + ], + "salesforce_organizations": [ + { + "filepath": "classpath:fixtures/common/repositories/salesforceOrganization/ActiveSalesforceOrganization.json" + }, + { + "filepath": "classpath:fixtures/common/repositories/salesforceOrganization/DeletedSalesforceOrganization.json" + } + ] + }, + "testPostSalesforceConnectDisconnectedUserSignup": { + "salesforce_users": [ + { + "filepath": "classpath:fixtures/common/repositories/salesforceUser/DeletedSalesforceUser.json" + } + ], + "salesforce_organizations": [ + { + "filepath": "classpath:fixtures/common/repositories/salesforceOrganization/ActiveSalesforceOrganization.json" + } + ] + } +} \ No newline at end of file diff --git a/src/test/resources/fixtures/functional/controllers/crmOrganizationUserController/getCrmOrganizationUserList.fixtures.json b/src/test/resources/fixtures/functional/controllers/crmOrganizationUserController/getCrmOrganizationUserList.fixtures.json new file mode 100644 index 00000000..c6b52ec9 --- /dev/null +++ b/src/test/resources/fixtures/functional/controllers/crmOrganizationUserController/getCrmOrganizationUserList.fixtures.json @@ -0,0 +1,14 @@ +{ + "getCrmOrganizationUserList": { + "salesforce_users": [ + { + "filepath": "classpath:fixtures/common/repositories/salesforceUser/ActiveSalesforceUser.json" + } + ], + "salesforce_oauth_tokens": [ + { + "filepath": "classpath:fixtures/common/repositories/salesforceOauthToken/ActiveSalesforceOauthToken.json" + } + ] + } + } \ No newline at end of file diff --git a/src/test/resources/fixtures/functional/controllers/suggestionsController/postCrmActionsSuggestions.fixtures.json b/src/test/resources/fixtures/functional/controllers/suggestionsController/postCrmActionsSuggestions.fixtures.json new file mode 100644 index 00000000..874c0367 --- /dev/null +++ b/src/test/resources/fixtures/functional/controllers/suggestionsController/postCrmActionsSuggestions.fixtures.json @@ -0,0 +1,9 @@ +{ + "testPostCrmActionsSuggestions": { + "salesforce_users": [ + { + "filepath": "classpath:fixtures/common/repositories/salesforceUser/ActiveSalesforceUser.json" + } + ] + } +} \ No newline at end of file diff --git a/src/test/resources/fixtures/functional/controllers/userController/getCurrentUser.fixtures.json b/src/test/resources/fixtures/functional/controllers/userController/getCurrentUser.fixtures.json new file mode 100644 index 00000000..ce145085 --- /dev/null +++ b/src/test/resources/fixtures/functional/controllers/userController/getCurrentUser.fixtures.json @@ -0,0 +1,9 @@ +{ + "getCurrentUser": { + "salesforce_users": [ + { + "filepath": "classpath:fixtures/common/repositories/salesforceUser/ActiveSalesforceUser.json" + } + ] + } +} \ No newline at end of file diff --git a/src/test/resources/fixtures/repositories/salesforceOauthToken/ActiveSalesforceOauthToken.json b/src/test/resources/fixtures/repositories/salesforceOauthToken/ActiveSalesforceOauthToken.json deleted file mode 100644 index b01de72d..00000000 --- a/src/test/resources/fixtures/repositories/salesforceOauthToken/ActiveSalesforceOauthToken.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "accessToken": "AQICAHh8THF/qGARx8DUe8vJOd05vKEx5gSfpBLKTZxcuYkMbwFWjWxrd+Vi8AJcNygAUAkxAAAA0zCB0AYJKoZIhvcNAQcGoIHCMIG/AgEAMIG5BgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDOiBQbHbADXAUCKszwIBEICBi8Mxso89hUZxuO/sab9tzOLCsTWNd6XMMSruXdmxTsjnCVIuuFmO9xE/dIlfLgdEOYx8ItFQ1yY1yHK0Mf5v9ioQZsjEqBloFwt6a0Zpu4ZPds3xd5SlhYb/DUOvQr2CeIw7c4DMCbhC3D4n3PPWwFrEAxwjbyZu6EuwKSCAJX8WNB/5SZyCWbF+lHo=", - "refreshToken": "AQICAHh8THF/qGARx8DUe8vJOd05vKEx5gSfpBLKTZxcuYkMbwGt67XnwFLbkXZGUHGMAbT+AAAAuTCBtgYJKoZIhvcNAQcGoIGoMIGlAgEAMIGfBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDEP3/y2MUdnniQUOTwIBEIByVjHYvSlaSx8QoDbRN7bN2tYTnqG73wuEr0Ak1CTROx3s5ztbyDqhlXvcQSJpeoP4dzbri9JFXQzvc18Y4SuW6Fkb7flN10LuxNuQWvCd5Meo4fPTovjzyVOXt9Qoy4BYJ8eaWZ9IaxpdYjyqzg0be1/O", - "updatedAt": "2023-08-17T09:53:08.331Z", - "signature": "FCGHH2fMCTciwY6zwcrBin10xO8dDKhYwp/FLavAQ8U=", - "instanceUrl": "https://truesparrowsystemspvtltd--dev.sandbox.my.salesforce.com", - "idToken": "eyJraWQiOiIyNDQiLCJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJhdF9oYXNoIjoiRXlBLTRWX2xoQlRHVzZ4QW40ZmtJQSIsInN1YiI6Imh0dHBzOi8vdGVzdC5zYWxlc2ZvcmNlLmNvbS9pZC8wMEQxZTAwMDAwMDBOUllFQTIvMDA1NWkwMDAwMEFVeFFIQUExIiwiYXVkIjoiM01WRzlaVUdnMTBIaDIyN01MUE0zd2lMTWxtMTQ5MTJvRHFkbDRzQkFnVjNyVUw4ODBYbWdZRVh6S0RZa3VlbEhQSmF4TnRjanBYdlkwYk1qVVNaWiIsImlzcyI6Imh0dHBzOi8vdGVzdC5zYWxlc2ZvcmNlLmNvbSIsImV4cCI6MTY5MjI2NjEwNywiaWF0IjoxNjkyMjY1OTg3fQ.ulBzHKfMkd0TtGOF60dgbae4ORHHilBakkfT9iB-FciFTJPAd87Jy5uKOndISDiqNtkcPDFJaH_1Sjl16DLHcLS8vNbzQFmJtoQt0tYEWT7_sQ7xq5DYnZKOVgPUnZyHh0UC7Y3a7UbQD1IQ-sMPtNZZn_jWGL_6UOcEotlh_Gg3DxrylkBGpS82h0U9CZyKRe2yZ0SE_6-FW_xFasSXfEF_9e6x6NAzJAEULpU4iLp2t7cHtjoKwIgFsqk3gasua26y74uxdZCbFOmivyBudIDSVzmIYmr_0kVezOZ5QEZgdVjkQUhmioGUFed5wzoNeUaYPYqRjGLeDHiYjSzBiSGRPil6Oimrwwre1W0Qurnw5vwcjgae3-Jf3mbP-IlMN4XZxiEG2fjeXBIO8-sMCLJfun4-RG48A5UYukQwVSB062h-1exmZ9ugv2wetUKAEaLKDLyKx4CWgoI7hLibXDB345IfOmsFNfOKCwHVE7_s-PHlyA70-_QihrDvuADfdjFR6LesRFMis4ns96ISOyR5h0nDcXaNufR-YKeHA90QgLXSeNs21B89tCvdMulnj2noRatDQcMRESxbiV6O98vJV27Z8E1UVJdwsHXQJZAxLd4Ib5uXbZNjqC10tVFLzT9KxgTYI8KtS9TxQ4DekPfXbziS9IC3c--ImEbSeio", - "createdAt": "2023-08-17T09:53:08.331Z", - "identityUrl": "https://test.salesforce.com/id/00D1e0000000NRYEA2/0055i00000AUxQHAA1", - "issuedAt": "1692265987212", - "externalUserId": "00112233445566AAA1", - "status": "ACTIVE" -} \ No newline at end of file diff --git a/src/test/resources/fixtures/repositories/salesforceOrganization/ActiveSalesforceOrganization.json b/src/test/resources/fixtures/repositories/salesforceOrganization/ActiveSalesforceOrganization.json deleted file mode 100644 index e9ed13e5..00000000 --- a/src/test/resources/fixtures/repositories/salesforceOrganization/ActiveSalesforceOrganization.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "externalOrganizationId": "00D2v0000012gJjEAI", - "status": "ACTIVE" -} \ No newline at end of file diff --git a/src/test/resources/fixtures/repositories/salesforceUser/ActiveSalesforceUser.json b/src/test/resources/fixtures/repositories/salesforceUser/ActiveSalesforceUser.json deleted file mode 100644 index d9e6f056..00000000 --- a/src/test/resources/fixtures/repositories/salesforceUser/ActiveSalesforceUser.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "externalUserId": "00112233445566AAA1", - "identityUrl": "https://test.salesforce.com/id/00D2v0000012gJjEAI/00112233445566AAA1", - "externalOrganizationId": "00D2v0000012gJjEAI", - "name": "Test User", - "email": "test@test.com", - "userKind": "SALESFORCE", - "cookieToken": "RSfDRwhQvIeFwJ9AAnv1ed3YN3ob9e9R0vSgKbsaxFcHFKwuyH6DiWVpD4orMrHQ3Kftns15kq04igHvaPaZLXbzWxQUOgtMr1lYZWBBF3s=", - "encryptionSalt": "EHpifHQeXUJcz1LtEDdx2Z/yB2qoHNTGfcWZQQlxsOeHGICDip9rPMpzG+LVtUOh", - "status": "ACTIVE" -} \ No newline at end of file diff --git a/src/test/resources/fixtures/unit/repositories/salesforceOauthTokenRepository.fixture.json b/src/test/resources/fixtures/unit/repositories/salesforceOauthTokenRepository.fixture.json new file mode 100644 index 00000000..b3298e5e --- /dev/null +++ b/src/test/resources/fixtures/unit/repositories/salesforceOauthTokenRepository.fixture.json @@ -0,0 +1,23 @@ +{ + "testUpdate": { + "salesforce_oauth_tokens": [ + { + "filepath": "classpath:fixtures/common/repositories/salesforceOauthToken/ActiveSalesforceOauthToken.json" + } + ] + }, + "testUpdateWithNullAttributes": { + "salesforce_oauth_tokens": [ + { + "filepath": "classpath:fixtures/common/repositories/salesforceOauthToken/ActiveSalesforceOauthToken.json" + } + ] + }, + "testValidGetSalesforceOauthTokenByExternalUserId": { + "salesforce_oauth_tokens": [ + { + "filepath": "classpath:fixtures/common/repositories/salesforceOauthToken/ActiveSalesforceOauthToken.json" + } + ] + } +} \ No newline at end of file diff --git a/src/test/resources/fixtures/unit/repositories/salesforceOrganizationRepository.json b/src/test/resources/fixtures/unit/repositories/salesforceOrganizationRepository.json new file mode 100644 index 00000000..182d0bdc --- /dev/null +++ b/src/test/resources/fixtures/unit/repositories/salesforceOrganizationRepository.json @@ -0,0 +1,9 @@ +{ + "testValidGetSalesforceOrganizationByExternalOrganizationId": { + "salesforce_organizations": [ + { + "filepath": "classpath:fixtures/common/repositories/salesforceOrganization/ActiveSalesforceOrganization.json" + } + ] + } +} \ No newline at end of file diff --git a/src/test/resources/fixtures/unit/repositories/salesforceUserRepository.json b/src/test/resources/fixtures/unit/repositories/salesforceUserRepository.json new file mode 100644 index 00000000..6690113f --- /dev/null +++ b/src/test/resources/fixtures/unit/repositories/salesforceUserRepository.json @@ -0,0 +1,9 @@ +{ + "testValidGetSalesforceUserByExternalUserId": { + "salesforce_users": [ + { + "filepath": "classpath:fixtures/common/repositories/salesforceUser/ActiveSalesforceUser.json" + } + ] + } +} \ No newline at end of file diff --git a/start_local.sh b/start_local.sh deleted file mode 100644 index f1c01049..00000000 --- a/start_local.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env bash - -. ./set_env_vars.sh -./mvnw spring-boot:run \ No newline at end of file