diff --git a/.gitignore b/.gitignore
index 650c91720..e1d20359d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,7 +2,7 @@
out/
!**/src/main/**/out/
!**/src/test/**/out/
-.idea/easycode.ignore
+../../Downloads/plaid-integration/data/.idea/easycode.ignore
target/
@@ -28,5 +28,7 @@ bin/
### VS Code ###
.vscode/
-### Mac OS ###
-.DS_Store
\ No newline at end of file
+### OS ###
+.DS_Store
+
+.env
diff --git a/.idea/.gitignore b/.idea/.gitignore
index 13566b81b..e69de29bb 100644
--- a/.idea/.gitignore
+++ b/.idea/.gitignore
@@ -1,8 +0,0 @@
-# Default ignored files
-/shelf/
-/workspace.xml
-# Editor-based HTTP Client requests
-/httpRequests/
-# Datasource local storage ignored files
-/dataSources/
-/dataSources.local.xml
diff --git a/.idea/checkstyle-idea.xml b/.idea/checkstyle-idea.xml
index aa01ae2cb..8d0f35699 100644
--- a/.idea/checkstyle-idea.xml
+++ b/.idea/checkstyle-idea.xml
@@ -1,17 +1,19 @@
- 10.18.0
+ 10.18.1JavaOnly
+ true
+
diff --git a/.idea/checkstyleidea-libs/readme.txt b/.idea/checkstyleidea-libs/readme.txt
new file mode 100644
index 000000000..e2a14a6fd
--- /dev/null
+++ b/.idea/checkstyleidea-libs/readme.txt
@@ -0,0 +1,6 @@
+This folder contains libraries copied from the "lab-5" project.
+It is managed by the CheckStyle-IDEA IDE plugin.
+Do not modify this folder while the IDE is running.
+When the IDE is stopped, you may delete this folder at any time. It will be recreated as needed.
+In order to prevent the CheckStyle-IDEA IDE plugin from creating this folder,
+uncheck the "Copy libraries from project directory" option in the CheckStyle-IDEA settings dialog.
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
new file mode 100644
index 000000000..d73481b1b
--- /dev/null
+++ b/.idea/compiler.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
new file mode 100644
index 000000000..63e900193
--- /dev/null
+++ b/.idea/encodings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 000000000..03d9549ea
--- /dev/null
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml
index 712ab9d98..2aaee56b6 100644
--- a/.idea/jarRepositories.xml
+++ b/.idea/jarRepositories.xml
@@ -16,5 +16,10 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 000000000..f0f82878c
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/shelf/Uncommitted_changes_before_Checkout_at_2024-11-30_1_28_p_m__[Changes]/shelved.patch b/.idea/shelf/Uncommitted_changes_before_Checkout_at_2024-11-30_1_28_p_m__[Changes]/shelved.patch
new file mode 100644
index 000000000..33a658499
--- /dev/null
+++ b/.idea/shelf/Uncommitted_changes_before_Checkout_at_2024-11-30_1_28_p_m__[Changes]/shelved.patch
@@ -0,0 +1,76 @@
+Index: .idea/workspace.xml
+IDEA additional info:
+Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP
+<+>\r\n\r\n \r\n
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties
new file mode 100644
index 000000000..d58dfb70b
--- /dev/null
+++ b/.mvn/wrapper/maven-wrapper.properties
@@ -0,0 +1,19 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+wrapperVersion=3.3.2
+distributionType=only-script
+distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip
diff --git a/README.md b/README.md
index 8c4770018..ddb7fba61 100644
--- a/README.md
+++ b/README.md
@@ -1,181 +1,329 @@
-# Lab 5: Logout
+# OSIRIS: Plaid Banking App
+
+## Author:
+### Mahan Hooshmandkhayat:
+###### Welcome View for Team Use Case, Add Bank Account Use Case, MySQL Database Integration, Spring Boot Server Integration, Frontend Integration Using JavaScript and Html
+### Divnoor Pal Singh Chatha:
+###### Verify View for Team Use Case, GrabTransaction UseCase, ViewExpenses UseCase, JFreeChart Integration
+### Shah Jalalul Kabir:
+###### CAPTCHA Signup Verification System for Team Use Case, Generate Assets Report UseCase
+### Christopher Han Roy:
+##### Dashboard view for Team Use Case, Buget for personal use case
## Preamble
-In the current homework, you added code to the login use case to save the currently-logged-in
-user by saving the user in the Data Access Layer. You also added a unit test for this.
+The project gets financial information from any bank account in Canada or USA for specific user, and analyzes the
+data for it to be displayed onto various pages for your viewing pleasure.
+
+This project is intriguing because it provides a seamless way to access and analyze financial data from any Canadian or
+U.S. bank account, offering personalized insights in an engaging and user-friendly format. By simplifying complex
+financial information and presenting it visually across multiple pages, it empowers users to make informed decisions with ease.
+
+This project solves the problem of fragmented financial management by consolidating data from various bank accounts into
+one accessible platform. It eliminates the need for manual tracking and analysis of financial information, saving users
+time and reducing errors. Additionally, it provides clear, actionable insights, helping users better understand and manage their finances.
+
+
+
+## Features of the software
+Uses the program uses OkHttp to allow a user to make an OSIRIS account using a username and password along with a
+verfication system, which done by the Brevo API. All the servers are made using the Spring Boot API. Once the user is
+logged in, the user can add a bank account using the bank account button on the home page. This is where the Plaid API
+is called and allows you to add a bank account. Once Bank account is added, the user can navigate to the various pages
+in the homepage to view various analysis regarding thier bank account. Features like looking at a graph of your
+Non-Essential vs Essential spending over the last 30 days or viewing your spending habits broken down into how much
+money you spend each month.
+
+## Installation Instructions
+You should currently be viewing this README in the GitHub repository: https://github.com/Mahan-H/CSC207_Project.
+Ensure you're logged into GitHub.
+
+In the top-right corner of the page, you'll see a Fork button. Click it to create your
+own copy of the repository, which you can modify as needed. On the next page, confirm the fork creation. This will
+generate your own version of the OSIRIS repository on GitHub. Once you’ve created your fork, close this page, navigate
+to your forked repository, and follow the subsequent instructions.
+
+Next, you’ll need a local copy of your forked repository on your computer. This process is called cloning the
+repository. In your forked repository on GitHub, click the green Code button and copy the HTTPS URL by clicking the
+small copy icon (two overlapping rectangles).
+
+Open IntelliJ. If you have a project already open, close it. From the main screen, select Get From VCS. Paste the copied
+URL into the appropriate field. You can use the default location on your computer to save the project or choose a custom
+directory.
+
+Note: If you clone the repository again later, you’ll need to select a new directory, as duplicate projects
+cannot exist in the same directory. Click Clone and IntelliJ will set up your project. If prompted, you may need to
+configure an access token to clone the repository, especially if this was not completed during the initial software
+setup on Quercus.
+
+With the repository cloned to your computer, proceed to work through the rest of the activity in
+IntelliJ. Open this README.md file in IntelliJ, and toggle between the editor, editor+preview, or preview view using
+the options in the top-right corner. You can take notes directly in the file or simply review it in the preview view.
+
+Make sure you load the maven project and make sure that the pom.xml file is the following:
+
+```
+
+
+ 4.0.0
+
+ com.example
+ plaid-integration
+ 1.0.0
+ jar
+
+ plaid-integration
+ Integration of Plaid API with Spring Boot
+
+
+ 17
+ 3.4.0
+ 2.11.0
+ 11
+ 11
+ UTF-8
+
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 3.4.0
+
+
+
+
+
+ plaid-repo
+ https://plaid.com/maven
+
+
+
+
+
+ org.mockito
+ mockito-core
+ 5.0.0
+ test
+
+
+ org.jfree
+ jfreechart
+ 1.5.3
+
+
+ org.junit.jupiter
+ junit-jupiter
+ 5.8.1
+ test
+
+
+ com.plaid
+ plaid-java
+ 29.0.0
+
+
+ org.apache.httpcomponents
+ httpclient
+ 4.5.13
+
+
+ org.json
+ json
+ 20240303
+
+
+ com.squareup.okhttp3
+ okhttp
+ 4.12.0
+
+
+ junit
+ junit
+ 4.13.1
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter
+ 5.8.1
+ test
+
+
+ com.sendinblue
+ sib-api-v3-sdk
+ 7.0.0
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ 2.18.1
+
+
+ org.slf4j
+ slf4j-simple
+ 1.7.32
+
+
+ com.sun.mail
+ javax.mail
+ 1.6.2
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+
+
+ com.google.code.gson
+ gson
+ ${gson.version}
+
+
+
+
+ org.springframework.boot
+ spring-boot-devtools
+ runtime
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ com.h2database
+ h2
+
+
+ org.projectlombok
+ lombok
+ annotationProcessor
+
+
+ org.mockito
+ mockito-core
+ 5.14.2
+ test
+
+
+ org.mockito
+ mockito-junit-jupiter
+ 5.14.2
+ test
+
+
+
+ org.jacoco
+ jacoco-maven-plugin
+ 0.8.12
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.11.0
+
+ ${java.version}
+ ${java.version}
+ UTF-8
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+ ${spring.boot.version}
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 3.5.2
+
+
+ org.jacoco
+ jacoco-maven-plugin
+ 0.8.8
+
+
+
+ prepare-agent
+
+
+
+ report
+ test
+
+ report
+
+
+
+
+
+
+
-In this lab, you will complete a logout use case as a team. You will also begin to discuss your project
-and the use cases that need to be implemented.
-
-We have created all the Clean Architecture classes necessary for the logout use case.
-
-By Friday, your team will submit:
-- your completed lab code [for credit]
-- a draft of your project blueprint proposal. [required, but not for credit]
-
-# Phase 2 [for credit]
-_(recall, Phase 1 was your solo task of adding the storage of the currently-logged-in user)_
-
-## Task 0: Fork this repo on GitHub
-**To get started, one team member should fork this repo on GitHub and share it with the team.
-All of you should then clone it.**
-
-* * *
-
-Suggested logistics: One of you should invite the others to collaborate on their fork of the
-original repo on GitHub. You can do this in your repo on GitHub under `Settings -> Collaborators`.
-This will allow you to push branches to a common repo and then use pull requests to contribute
-your code and review. To prevent others from pushing directly to the main branch,
-we recommend you set branch protection rules on GitHub. Below are how the settings might look if you
-add branch protection rules:
-
-
-
-* * *
-
-Open the project in IntelliJ and make sure you can successfully run `app/Main.java`.
-Note: you may need to set the Project SDK in the `Project Structure...` menu, and possibly
-also manually link the Maven project, as you did in Phase 1.
-
-## Task 1: Understanding the Program
-
-You may notice that we have refactored the CA engine code _slightly_ since Phase 1, but the
-way we build the engine is drastically different: we have switched from using Factories to
-using the Builder design pattern, which we'll be discussing in lecture soon.
-
-Open up `app.Main` and read it as a team.
-- What are the Views and what are the current Use Cases?
-- Which Uses Cases are triggered from each View?
-- Which version of the DAO is `app.Main` using?
-
-The major change since Phase 1 is that we have added the `app.AppBuilder` class which makes
-it easier to understand how our CA engine is being constructed — it also makes `app.Main` nice and concise!
-- Why do all those `addX` methods end in `return this;`?
-
-Run the program and make sure the signup and login Use Cases work.
-
-Currently, you'll notice that the "Log Out" button still doesn't actually log you out. It's time to fix
-that button, which is part of the `LoggedInView`.
-We have created all the classes for you, but some of the code is missing.
-As a team, your task is to fill in the missing code so that the Logout Use Case is implemented.
-**The next part of the readme describes how your team will do this.**
-
-* * *
-
-**Your team will know when you are done when:**
-
-- Clicking the "Log Out" button takes the user back to the Login View when you use the program.
-- The provided `LogoutInteractorTest` test passes.
-
-The "Log Out" button is an instance variable in class `LoggedInVew`. Go find it.
-Also look at the `interface_adapter.change_password.LoggedInViewModel`, which contains any
-data showing on the `LoggedInVew`.
-
-* * *
-
-## Task 2: Dividing up the work
-
-There are `TODO` comments left in the files
-Recall that you can use the TODO tool window to conveniently pull up a complete list.
-
-Once the TODOs are all complete, the "Log Out" button _should_ work!
-
-As a team, split up the TODOs (see below) between the members of your team.
-
-There are TODOs in seven of the files.
-Make sure each member has at least one TODO which they will be responsible for completing.
-If your team prefers to work in pairs, that is fine too. Your individual branches
-will not be graded for this — only the final, working version.
-
-The TODOs are summarized below (by file) to help your team decide how to split them up:
-
-* * *
-
-- `Main.java`
-
- - [ ] TODO: add the Logout Use Case to the app using the appBuilder
-
-* * *
-
-- `LoggedInView.java` (tip: refer to the other views for similar code)
-
- - [ ] TODO: save the logout controller in the instance variable.
- - [ ] TODO: execute the logout use case through the Controller
-
-* * *
-
-- `LogoutController.java` (tip: refer to the other controllers for similar code)
-
- - [ ] TODO: Save the interactor in the instance variable.
- - [ ] TODO: run the use case interactor for the logout use case
-
-* * *
-
-- `LogoutInputData.java` (should be done with the LogoutInteractor TODOs below)
-
- - [ ] TODO: save the current username in an instance variable and add a getter.
-
-- `LogoutInteractor.java` (tip: refer to `ChangePasswordInteractor.java` for similar code)
-
- - [ ] TODO: save the DAO and Presenter in the instance variables.
- - [ ] TODO: implement the logic of the Logout Use Case
-
-* * *
-
-- `LogoutOutputData.java`
-
- - [ ] TODO: save the parameters in the instance variables.
-
-* * *
-
-- `LogoutPresenter.java` (tip: refer to `SignupPresenter.java` for similar code)
-
- - [ ] TODO: assign to the three instance variables.
- - [ ] TODO: have prepareSuccessView update the LoggedInState
- - [ ] TODO: have prepareSuccessView update the LoginState
-
-* * *
-
-1. Make a branch named the first part of your UofT email address, everything before the `@`.
-For example, if your email address is `paul.gries@mail.utoronto.ca`, then the branch name would
-be `paul.gries`.
-
-Make sure you switch to the new branch.
-
-In the terminal, this would look like below, but replaced with your own information:
```
-git branch paul.gries
-git switch paul.gries
+Once you have the pom.xml file, you need to download the MySQL database. You can download the MySQL database from here https://dev.mysql.com/downloads/mysql/.
+Once you have downloaded the MySQL database, you need to follow the instructions that will be provided to you in the MySQL installer.
+Once you have installed and configured the MySQL database, you need to create a local database in your code editor. We will showcase how you can do this in IntelliJ.
+1. Open IntelliJ and click on the Database tab on the right side of the screen.
+
+
+2. Click on the + icon and select Data Source -> MySQL.
+
+!
+3. You will be prompted to enter the following information:
+ - Host: localhost
+ - Port
+ - User: root
+ - Password: Your password
+ - Database: Your database name
+ - Click on the Test Connection button to ensure that the connection is successful.
+ - Click on the OK button to save the configuration.
+
+
+4. You should now see your database in the Database tab.
+
+
+5. You can now create a new schema in your database by right-clicking on the database and selecting New -> Schema.
+
+
+5. You can now create a new table in your database by right-clicking on the database and selecting New -> Table.
+
+
+6. Once you have created the table, you can now connect your database to your Spring Boot application. You can do this by adding the following code to your application.properties file:
+ - spring.datasource.url=jdbc:mysql://localhost:3306/your_database_name
+ - spring.datasource.username=root
+ - spring.datasource.password=your_password
+ - spring.jpa.hibernate.ddl-auto=update
+ - spring.jpa.show-sql=true
+ - spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
+7. You can now run your Spring Boot application and test the connection to your database.
+8. You can now start adding data to your database and retrieving data from your database in your Spring Boot application.
+
+Now you are ready to run the program. You can look at usage guide for instructions on how to run the program.
```
+## Usage Guide
-2. Complete your assigned TODOs and make a pull request on GitHub. In your pull request,
- briefly describe what your TODOs were and how you implemented them. If you aren't sure
- about part of it, include this in your pull request so everyone knows what to look
- for when reviewing — or you can of course discuss with your team before making your
- pull request since you are physically working in the same space.
- - **Important: don't push any changes to the `.idea` folder, as that
- may cause issues for your other teammates, as some files contain
- configurations specific to your individual IntelliJ projects.**
+## Section for feedback (how to give feedback on the project):
+#### Email: mahan.hooshmandkhayat@mail.utoronto.ca or divnoor.chatha@mail.utoronto.ca
+# YOU MAY NOT ADD ANY CONTRIBUTIONS TO THIS PROJECT!!!
-3. Review each other's pull requests to ensure each TODO is correctly implemented and that
- there are no Checkstyle issues in the files that were modified.
-
-4. Once all TODOs are completed, your team should debug as needed to ensure the
- correctness of the code. Setting a breakpoint where the log-out use case
- interactor starts its work will likely be a great place to start when debugging.
-
-And that's it; you now have a working Logout Use Case! Instructions for
-how to submit your work on MarkUs will be posted later.
-
-Your team should spend the rest of the lab working on your project blueprint.
* * *
-# Project Blueprint
-
-See Quercus for details about the project blueprint! By the end of the week,
-the goal is for your team to have a fully drafted blueprint so that your team
-will be ready to get started on your project after Reading Week.
+##### Licensing can be seen in LICENSE.txt
diff --git a/accessibility-report.md b/accessibility-report.md
new file mode 100644
index 000000000..35fd1efe8
--- /dev/null
+++ b/accessibility-report.md
@@ -0,0 +1,21 @@
+### For each Principle of Universal Design, write 2-3 sentences — or point form notes — explaining which features of your program adhere to that principle.
+
+**S:** The PieChartUtility class is solely responsible for creating and displaying pie charts. It does not handle transactions, user data, or presentation logic, adhering to SRP. The way we have laid out our use cases to adhere to clean architecture also shows this. For example, the Views for each use case are responsible for making the view page (should only change for changing the display)
+
+**O:** The PlaidDataAccessObject cannot be modified since it organizes functionality into methods with specific responsibilities, but it can be extended to support accessing different Plaid data. (fetching transactions, creating link tokens, etc). The entity package cannot be modified (as that will change what the entity represents), but we can add more entities to that package as our project expands.
+
+**L:** The GrabTransactionController returns a GrabTransactionOutputData, which can be changed to a String object without breaking the existing code. This means that the code will still run. The Controller is designed to work with different kinds of outputs so long as they are similar in behaviour. If what GrabTransactionController returned was changed, from GrabTransactionOutputData to a list of transactions, the rest of the code would not need to be changed. The rest of the code would operate like clockwork and this would be attributed to the controller not depending on a specific type. This means that it can work with general structures that can be changed without a massive failure.
+
+**I:** The inclusion of several interfaces within our application allows for compartmentalization of its overall functioning. Interfaces such as the VerifyInputBoundary, LoginInputBoundary, and SignupInputBoundary are all intended to work together to complete different parts of the user handling process. Thus, preventing a case where one class is required to implement methods that it itself does not utilize. Streamlining interactions between related files also allows segregation of interfaces. For example, the VerifyInteractor only interacts with files related to Verification, such as the VerifyInputBoundary and VerifyOutputBoundary, and not files involved in the signup or login process. Even then, the three aforementioned files have designated operations that could have potentially been amalgamated into one singular file. This is done to avoid unnecessary coupling and creates a system with easier maintenance.
+
+**D:** The AppBuilder class displays connections between high-level modules, such as Controllers and low-level modules like Interactors through abstract classes. Instead of having higher level modules feature concrete implementations, the injection of dependencies is conducted in the initialization process. This can ensure flexibility and modularity. For example, the VerifyController depends on VerifyInputBoundary, allowing the VerifyInteractor to be modified without alterations of the controller. This is another concept that improves the quality of the development process.
+
+
+### Write a paragraph (3-6 sentences) about who you would market your program towards, if you were to sell or license your program to customers. This could be a specific category of users, such as "students", or more vague, such as "people who like games". Try to give a bit more detail along with the category.
+
+The program would be best marketed towards young adults and adolescents, specifically university students. It would serve as a way for them to aggregate their financial data and create limitations for themselves. For many, university is a very crucial and expensive time. The latter point means they must retain their funds as carefully as possible, as not doing so can make a substantial difference. A majority of students do not have disposable funds, so a behaviour like budgeting would prove to be extremely helpful. An app such as Osiris, which can help students reduce unnecessary expenses and be more financially aware of what they expend funds on, can save them from inconveniences caused by a lack of funds. This could go as far as preventing them from skipping meals due to unavailability of funds, or even avoiding purchasing textbooks. It would prove to not only be a financially assisting app, but an app that indirectly benefits health and academics as well.
+
+
+### Write a paragraph about whether or not your program is less likely to be used by certain demographics. Your discussion here should be informed by the content of our embedded ethics modules this term.
+
+This app is most likely to be utilized by those who are not well versed in finance, such as students. It is made to be a simple application that allows users to understand their finances and figure out a solution by seeing everything in one place. Since the app does not require much technical knowledge, besides knowing one’s banking login information and creating and logging in with their login information for the app, it wouldn’t be the case where this app couldn't be inclusive of all populations. This app does however have a lack of information, since it is made to be simple, thus it wouldn’t be meant for those who are knowledgeable in finance and prefer to have much more intricate features when managing their finances. For example, having a way to make investments or the best way to collect interest on high profile accounts. This app was made with consideration of the people. By the people, we mean everybody. Not everything is obvious to everybody, so making it so that people can easily see the basic things they need to see was taken into consideration with this app. We even included a system to display diagrams so that people who prefer visualization over specifics could grasp a deeper mental understanding of certain statistics. For example, one may look at their spending and think nothing of it, but looking at a chart that shows equal red and blue should be alarming, since red would mean bad and blue would mean good.
diff --git a/homework-5.iml b/homework-5.iml
new file mode 100644
index 000000000..9e3449c9d
--- /dev/null
+++ b/homework-5.iml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/images/branch_protection_rules.png b/images/branch_protection_rules.png
deleted file mode 100644
index 35c6fb2f8..000000000
Binary files a/images/branch_protection_rules.png and /dev/null differ
diff --git a/img.png b/img.png
new file mode 100644
index 000000000..952830ad9
Binary files /dev/null and b/img.png differ
diff --git a/img_1.png b/img_1.png
new file mode 100644
index 000000000..f5f333e39
Binary files /dev/null and b/img_1.png differ
diff --git a/img_2.png b/img_2.png
new file mode 100644
index 000000000..633b7eaf6
Binary files /dev/null and b/img_2.png differ
diff --git a/img_3.png b/img_3.png
new file mode 100644
index 000000000..0be6c3163
Binary files /dev/null and b/img_3.png differ
diff --git a/img_4.png b/img_4.png
new file mode 100644
index 000000000..451b5eb33
Binary files /dev/null and b/img_4.png differ
diff --git a/img_5.png b/img_5.png
new file mode 100644
index 000000000..f8baf4ee5
Binary files /dev/null and b/img_5.png differ
diff --git a/mvnw b/mvnw
new file mode 100644
index 000000000..19529ddf8
--- /dev/null
+++ b/mvnw
@@ -0,0 +1,259 @@
+#!/bin/sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+# ----------------------------------------------------------------------------
+
+# ----------------------------------------------------------------------------
+# Apache Maven Wrapper startup batch script, version 3.3.2
+#
+# Optional ENV vars
+# -----------------
+# JAVA_HOME - location of a JDK home dir, required when download maven via java source
+# MVNW_REPOURL - repo url base for downloading maven distribution
+# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
+# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output
+# ----------------------------------------------------------------------------
+
+set -euf
+[ "${MVNW_VERBOSE-}" != debug ] || set -x
+
+# OS specific support.
+native_path() { printf %s\\n "$1"; }
+case "$(uname)" in
+CYGWIN* | MINGW*)
+ [ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")"
+ native_path() { cygpath --path --windows "$1"; }
+ ;;
+esac
+
+# set JAVACMD and JAVACCMD
+set_java_home() {
+ # For Cygwin and MinGW, ensure paths are in Unix format before anything is touched
+ if [ -n "${JAVA_HOME-}" ]; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ]; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ JAVACCMD="$JAVA_HOME/jre/sh/javac"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ JAVACCMD="$JAVA_HOME/bin/javac"
+
+ if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then
+ echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2
+ echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2
+ return 1
+ fi
+ fi
+ else
+ JAVACMD="$(
+ 'set' +e
+ 'unset' -f command 2>/dev/null
+ 'command' -v java
+ )" || :
+ JAVACCMD="$(
+ 'set' +e
+ 'unset' -f command 2>/dev/null
+ 'command' -v javac
+ )" || :
+
+ if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then
+ echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2
+ return 1
+ fi
+ fi
+}
+
+# hash string like Java String::hashCode
+hash_string() {
+ str="${1:-}" h=0
+ while [ -n "$str" ]; do
+ char="${str%"${str#?}"}"
+ h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296))
+ str="${str#?}"
+ done
+ printf %x\\n $h
+}
+
+verbose() { :; }
+[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; }
+
+die() {
+ printf %s\\n "$1" >&2
+ exit 1
+}
+
+trim() {
+ # MWRAPPER-139:
+ # Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds.
+ # Needed for removing poorly interpreted newline sequences when running in more
+ # exotic environments such as mingw bash on Windows.
+ printf "%s" "${1}" | tr -d '[:space:]'
+}
+
+# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties
+while IFS="=" read -r key value; do
+ case "${key-}" in
+ distributionUrl) distributionUrl=$(trim "${value-}") ;;
+ distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;;
+ esac
+done <"${0%/*}/.mvn/wrapper/maven-wrapper.properties"
+[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in ${0%/*}/.mvn/wrapper/maven-wrapper.properties"
+
+case "${distributionUrl##*/}" in
+maven-mvnd-*bin.*)
+ MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/
+ case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in
+ *AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;;
+ :Darwin*x86_64) distributionPlatform=darwin-amd64 ;;
+ :Darwin*arm64) distributionPlatform=darwin-aarch64 ;;
+ :Linux*x86_64*) distributionPlatform=linux-amd64 ;;
+ *)
+ echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2
+ distributionPlatform=linux-amd64
+ ;;
+ esac
+ distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip"
+ ;;
+maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;;
+*) MVN_CMD="mvn${0##*/mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;;
+esac
+
+# apply MVNW_REPOURL and calculate MAVEN_HOME
+# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/
+[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}"
+distributionUrlName="${distributionUrl##*/}"
+distributionUrlNameMain="${distributionUrlName%.*}"
+distributionUrlNameMain="${distributionUrlNameMain%-bin}"
+MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}"
+MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")"
+
+exec_maven() {
+ unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || :
+ exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD"
+}
+
+if [ -d "$MAVEN_HOME" ]; then
+ verbose "found existing MAVEN_HOME at $MAVEN_HOME"
+ exec_maven "$@"
+fi
+
+case "${distributionUrl-}" in
+*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;;
+*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;;
+esac
+
+# prepare tmp dir
+if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then
+ clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; }
+ trap clean HUP INT TERM EXIT
+else
+ die "cannot create temp dir"
+fi
+
+mkdir -p -- "${MAVEN_HOME%/*}"
+
+# Download and Install Apache Maven
+verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
+verbose "Downloading from: $distributionUrl"
+verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
+
+# select .zip or .tar.gz
+if ! command -v unzip >/dev/null; then
+ distributionUrl="${distributionUrl%.zip}.tar.gz"
+ distributionUrlName="${distributionUrl##*/}"
+fi
+
+# verbose opt
+__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR=''
+[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v
+
+# normalize http auth
+case "${MVNW_PASSWORD:+has-password}" in
+'') MVNW_USERNAME='' MVNW_PASSWORD='' ;;
+has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;;
+esac
+
+if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then
+ verbose "Found wget ... using wget"
+ wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl"
+elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then
+ verbose "Found curl ... using curl"
+ curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl"
+elif set_java_home; then
+ verbose "Falling back to use Java to download"
+ javaSource="$TMP_DOWNLOAD_DIR/Downloader.java"
+ targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName"
+ cat >"$javaSource" <<-END
+ public class Downloader extends java.net.Authenticator
+ {
+ protected java.net.PasswordAuthentication getPasswordAuthentication()
+ {
+ return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() );
+ }
+ public static void main( String[] args ) throws Exception
+ {
+ setDefault( new Downloader() );
+ java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() );
+ }
+ }
+ END
+ # For Cygwin/MinGW, switch paths to Windows format before running javac and java
+ verbose " - Compiling Downloader.java ..."
+ "$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java"
+ verbose " - Running Downloader.java ..."
+ "$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")"
+fi
+
+# If specified, validate the SHA-256 sum of the Maven distribution zip file
+if [ -n "${distributionSha256Sum-}" ]; then
+ distributionSha256Result=false
+ if [ "$MVN_CMD" = mvnd.sh ]; then
+ echo "Checksum validation is not supported for maven-mvnd." >&2
+ echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
+ exit 1
+ elif command -v sha256sum >/dev/null; then
+ if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c >/dev/null 2>&1; then
+ distributionSha256Result=true
+ fi
+ elif command -v shasum >/dev/null; then
+ if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then
+ distributionSha256Result=true
+ fi
+ else
+ echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2
+ echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
+ exit 1
+ fi
+ if [ $distributionSha256Result = false ]; then
+ echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2
+ echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2
+ exit 1
+ fi
+fi
+
+# unzip and move
+if command -v unzip >/dev/null; then
+ unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip"
+else
+ tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar"
+fi
+printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/mvnw.url"
+mv -- "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME"
+
+clean || :
+exec_maven "$@"
diff --git a/mvnw.cmd b/mvnw.cmd
new file mode 100644
index 000000000..249bdf382
--- /dev/null
+++ b/mvnw.cmd
@@ -0,0 +1,149 @@
+<# : batch portion
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License. You may obtain a copy of the License at
+@REM
+@REM http://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@REM ----------------------------------------------------------------------------
+@REM Apache Maven Wrapper startup batch script, version 3.3.2
+@REM
+@REM Optional ENV vars
+@REM MVNW_REPOURL - repo url base for downloading maven distribution
+@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
+@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output
+@REM ----------------------------------------------------------------------------
+
+@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0)
+@SET __MVNW_CMD__=
+@SET __MVNW_ERROR__=
+@SET __MVNW_PSMODULEP_SAVE=%PSModulePath%
+@SET PSModulePath=
+@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @(
+ IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B)
+)
+@SET PSModulePath=%__MVNW_PSMODULEP_SAVE%
+@SET __MVNW_PSMODULEP_SAVE=
+@SET __MVNW_ARG0_NAME__=
+@SET MVNW_USERNAME=
+@SET MVNW_PASSWORD=
+@IF NOT "%__MVNW_CMD__%"=="" (%__MVNW_CMD__% %*)
+@echo Cannot start maven from wrapper >&2 && exit /b 1
+@GOTO :EOF
+: end batch / begin powershell #>
+
+$ErrorActionPreference = "Stop"
+if ($env:MVNW_VERBOSE -eq "true") {
+ $VerbosePreference = "Continue"
+}
+
+# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties
+$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl
+if (!$distributionUrl) {
+ Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties"
+}
+
+switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) {
+ "maven-mvnd-*" {
+ $USE_MVND = $true
+ $distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip"
+ $MVN_CMD = "mvnd.cmd"
+ break
+ }
+ default {
+ $USE_MVND = $false
+ $MVN_CMD = $script -replace '^mvnw','mvn'
+ break
+ }
+}
+
+# apply MVNW_REPOURL and calculate MAVEN_HOME
+# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/
+if ($env:MVNW_REPOURL) {
+ $MVNW_REPO_PATTERN = if ($USE_MVND) { "/org/apache/maven/" } else { "/maven/mvnd/" }
+ $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace '^.*'+$MVNW_REPO_PATTERN,'')"
+}
+$distributionUrlName = $distributionUrl -replace '^.*/',''
+$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$',''
+$MAVEN_HOME_PARENT = "$HOME/.m2/wrapper/dists/$distributionUrlNameMain"
+if ($env:MAVEN_USER_HOME) {
+ $MAVEN_HOME_PARENT = "$env:MAVEN_USER_HOME/wrapper/dists/$distributionUrlNameMain"
+}
+$MAVEN_HOME_NAME = ([System.Security.Cryptography.MD5]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join ''
+$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME"
+
+if (Test-Path -Path "$MAVEN_HOME" -PathType Container) {
+ Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME"
+ Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
+ exit $?
+}
+
+if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) {
+ Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl"
+}
+
+# prepare tmp dir
+$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile
+$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir"
+$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null
+trap {
+ if ($TMP_DOWNLOAD_DIR.Exists) {
+ try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
+ catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
+ }
+}
+
+New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null
+
+# Download and Install Apache Maven
+Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
+Write-Verbose "Downloading from: $distributionUrl"
+Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
+
+$webclient = New-Object System.Net.WebClient
+if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) {
+ $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD)
+}
+[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
+$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null
+
+# If specified, validate the SHA-256 sum of the Maven distribution zip file
+$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum
+if ($distributionSha256Sum) {
+ if ($USE_MVND) {
+ Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties."
+ }
+ Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash
+ if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) {
+ Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property."
+ }
+}
+
+# unzip and move
+Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null
+Rename-Item -Path "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" -NewName $MAVEN_HOME_NAME | Out-Null
+try {
+ Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null
+} catch {
+ if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) {
+ Write-Error "fail to move MAVEN_HOME"
+ }
+} finally {
+ try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
+ catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
+}
+
+Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
diff --git a/mystyle.xml b/mystyle.xml
index 85dbcb535..3a2ad069e 100644
--- a/mystyle.xml
+++ b/mystyle.xml
@@ -720,7 +720,7 @@
-
+
@@ -903,4 +903,4 @@
-
\ No newline at end of file
+
diff --git a/plaid-integration.iml b/plaid-integration.iml
new file mode 100644
index 000000000..1ce7c9599
--- /dev/null
+++ b/plaid-integration.iml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index e5c1e881f..eef11e037 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,18 +4,70 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0
- edu.csc207
- homework-5
- 1.0-SNAPSHOT
+ com.example
+ plaid-integration
+ 1.0.0
+ jar
-
+ plaid-integration
+ Integration of Plaid API with Spring Boot
+
+
+ 17
+ 3.4.0
+ 2.11.0
+ 11
+ 11
+ UTF-8
+
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 3.4.0
+
+
+
+
+
+ plaid-repo
+ https://plaid.com/maven
+
+
+
+
+ org.mockito
+ mockito-core
+ 5.0.0
+ test
+
+
+ org.jfree
+ jfreechart
+ 1.5.3
+
+
+ org.junit.jupiter
+ junit-jupiter
+ 5.8.1
+ test
+
+
+ com.plaid
+ plaid-java
+ 29.0.0
+
+
+ org.apache.httpcomponents
+ httpclient
+ 4.5.13
+ org.jsonjson20240303
-
com.squareup.okhttp3okhttp
@@ -33,15 +85,135 @@
5.8.1test
+
+ com.sendinblue
+ sib-api-v3-sdk
+ 7.0.0
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ 2.18.1
+
+
+ org.slf4j
+ slf4j-simple
+ 1.7.32
+
+
+ com.sun.mail
+ javax.mail
+ 1.6.2
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
-
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+
+ com.google.code.gson
+ gson
+ ${gson.version}
+
+
+
+ org.springframework.boot
+ spring-boot-devtools
+ runtime
+
-
- 11
- 11
- UTF-8
-
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ com.h2database
+ h2
+
+
+ org.projectlombok
+ lombok
+ annotationProcessor
+
+
+ org.mockito
+ mockito-core
+ 5.14.2
+ test
+
+
+ org.mockito
+ mockito-junit-jupiter
+ 5.14.2
+ test
+
+
+
+ org.jacoco
+ jacoco-maven-plugin
+ 0.8.12
+
+
+
+ mysql
+ mysql-connector-java
+ 8.0.33
+
+
+
+
-
\ No newline at end of file
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.11.0
+
+ ${java.version}
+ ${java.version}
+ UTF-8
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+ ${spring.boot.version}
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 3.5.2
+
+
+ org.jacoco
+ jacoco-maven-plugin
+ 0.8.8
+
+
+
+ prepare-agent
+
+
+
+ report
+ test
+
+ report
+
+
+
+
+
+
+
diff --git a/src/main/java/app/AppBuilder.java b/src/main/java/app/AppBuilder.java
deleted file mode 100644
index e9eef5c81..000000000
--- a/src/main/java/app/AppBuilder.java
+++ /dev/null
@@ -1,185 +0,0 @@
-package app;
-
-import java.awt.CardLayout;
-
-import javax.swing.JFrame;
-import javax.swing.JPanel;
-import javax.swing.WindowConstants;
-
-import data_access.InMemoryUserDataAccessObject;
-import entity.CommonUserFactory;
-import entity.UserFactory;
-import interface_adapter.ViewManagerModel;
-import interface_adapter.change_password.ChangePasswordController;
-import interface_adapter.change_password.ChangePasswordPresenter;
-import interface_adapter.change_password.LoggedInViewModel;
-import interface_adapter.login.LoginController;
-import interface_adapter.login.LoginPresenter;
-import interface_adapter.login.LoginViewModel;
-import interface_adapter.logout.LogoutController;
-import interface_adapter.logout.LogoutPresenter;
-import interface_adapter.signup.SignupController;
-import interface_adapter.signup.SignupPresenter;
-import interface_adapter.signup.SignupViewModel;
-import use_case.change_password.ChangePasswordInputBoundary;
-import use_case.change_password.ChangePasswordInteractor;
-import use_case.change_password.ChangePasswordOutputBoundary;
-import use_case.login.LoginInputBoundary;
-import use_case.login.LoginInteractor;
-import use_case.login.LoginOutputBoundary;
-import use_case.logout.LogoutInputBoundary;
-import use_case.logout.LogoutInteractor;
-import use_case.logout.LogoutOutputBoundary;
-import use_case.signup.SignupInputBoundary;
-import use_case.signup.SignupInteractor;
-import use_case.signup.SignupOutputBoundary;
-import view.LoggedInView;
-import view.LoginView;
-import view.SignupView;
-import view.ViewManager;
-
-/**
- * The AppBuilder class is responsible for putting together the pieces of
- * our CA architecture; piece by piece.
- *
- * This is done by adding each View and then adding related Use Cases.
- */
-// Checkstyle note: you can ignore the "Class Data Abstraction Coupling"
-// and the "Class Fan-Out Complexity" issues for this lab; we encourage
-// your team to think about ways to refactor the code to resolve these
-// if your team decides to work with this as your starter code
-// for your final project this term.
-public class AppBuilder {
- private final JPanel cardPanel = new JPanel();
- private final CardLayout cardLayout = new CardLayout();
- // thought question: is the hard dependency below a problem?
- private final UserFactory userFactory = new CommonUserFactory();
- private final ViewManagerModel viewManagerModel = new ViewManagerModel();
- private final ViewManager viewManager = new ViewManager(cardPanel, cardLayout, viewManagerModel);
-
- // thought question: is the hard dependency below a problem?
- private final InMemoryUserDataAccessObject userDataAccessObject = new InMemoryUserDataAccessObject();
-
- private SignupView signupView;
- private SignupViewModel signupViewModel;
- private LoginViewModel loginViewModel;
- private LoggedInViewModel loggedInViewModel;
- private LoggedInView loggedInView;
- private LoginView loginView;
-
- public AppBuilder() {
- cardPanel.setLayout(cardLayout);
- }
-
- /**
- * Adds the Signup View to the application.
- * @return this builder
- */
- public AppBuilder addSignupView() {
- signupViewModel = new SignupViewModel();
- signupView = new SignupView(signupViewModel);
- cardPanel.add(signupView, signupView.getViewName());
- return this;
- }
-
- /**
- * Adds the Login View to the application.
- * @return this builder
- */
- public AppBuilder addLoginView() {
- loginViewModel = new LoginViewModel();
- loginView = new LoginView(loginViewModel);
- cardPanel.add(loginView, loginView.getViewName());
- return this;
- }
-
- /**
- * Adds the LoggedIn View to the application.
- * @return this builder
- */
- public AppBuilder addLoggedInView() {
- loggedInViewModel = new LoggedInViewModel();
- loggedInView = new LoggedInView(loggedInViewModel);
- cardPanel.add(loggedInView, loggedInView.getViewName());
- return this;
- }
-
- /**
- * Adds the Signup Use Case to the application.
- * @return this builder
- */
- public AppBuilder addSignupUseCase() {
- final SignupOutputBoundary signupOutputBoundary = new SignupPresenter(viewManagerModel,
- signupViewModel, loginViewModel);
- final SignupInputBoundary userSignupInteractor = new SignupInteractor(
- userDataAccessObject, signupOutputBoundary, userFactory);
-
- final SignupController controller = new SignupController(userSignupInteractor);
- signupView.setSignupController(controller);
- return this;
- }
-
- /**
- * Adds the Login Use Case to the application.
- * @return this builder
- */
- public AppBuilder addLoginUseCase() {
- final LoginOutputBoundary loginOutputBoundary = new LoginPresenter(viewManagerModel,
- loggedInViewModel, loginViewModel);
- final LoginInputBoundary loginInteractor = new LoginInteractor(
- userDataAccessObject, loginOutputBoundary);
-
- final LoginController loginController = new LoginController(loginInteractor);
- loginView.setLoginController(loginController);
- return this;
- }
-
- /**
- * Adds the Change Password Use Case to the application.
- * @return this builder
- */
- public AppBuilder addChangePasswordUseCase() {
- final ChangePasswordOutputBoundary changePasswordOutputBoundary =
- new ChangePasswordPresenter(loggedInViewModel);
-
- final ChangePasswordInputBoundary changePasswordInteractor =
- new ChangePasswordInteractor(userDataAccessObject, changePasswordOutputBoundary, userFactory);
-
- final ChangePasswordController changePasswordController =
- new ChangePasswordController(changePasswordInteractor);
- loggedInView.setChangePasswordController(changePasswordController);
- return this;
- }
-
- /**
- * Adds the Logout Use Case to the application.
- * @return this builder
- */
- public AppBuilder addLogoutUseCase() {
- final LogoutOutputBoundary logoutOutputBoundary = new LogoutPresenter(viewManagerModel,
- loggedInViewModel, loginViewModel);
-
- final LogoutInputBoundary logoutInteractor =
- new LogoutInteractor(userDataAccessObject, logoutOutputBoundary);
-
- final LogoutController logoutController = new LogoutController(logoutInteractor);
- loggedInView.setLogoutController(logoutController);
- return this;
- }
-
- /**
- * Creates the JFrame for the application and initially sets the SignupView to be displayed.
- * @return the application
- */
- public JFrame build() {
- final JFrame application = new JFrame("Login Example");
- application.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
-
- application.add(cardPanel);
-
- viewManagerModel.setState(signupView.getViewName());
- viewManagerModel.firePropertyChanged();
-
- return application;
- }
-}
diff --git a/src/main/java/app/Main.java b/src/main/java/app/Main.java
deleted file mode 100644
index bef63ad7a..000000000
--- a/src/main/java/app/Main.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package app;
-
-import javax.swing.JFrame;
-
-/**
- * The Main class of our application.
- */
-public class Main {
- /**
- * Builds and runs the CA architecture of the application.
- * @param args unused arguments
- */
- public static void main(String[] args) {
- final AppBuilder appBuilder = new AppBuilder();
- // TODO: add the Logout Use Case to the app using the appBuilder
- final JFrame application = appBuilder
- .addLoginView()
- .addSignupView()
- .addLoggedInView()
- .addSignupUseCase()
- .addLoginUseCase()
- .addChangePasswordUseCase()
- .build();
-
- application.pack();
- application.setVisible(true);
- }
-}
diff --git a/src/main/java/data_access/DBUserDataAccessObject.java b/src/main/java/data_access/DBUserDataAccessObject.java
deleted file mode 100644
index 377ee6e7e..000000000
--- a/src/main/java/data_access/DBUserDataAccessObject.java
+++ /dev/null
@@ -1,165 +0,0 @@
-package data_access;
-
-import java.io.IOException;
-
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import entity.User;
-import entity.UserFactory;
-import okhttp3.MediaType;
-import okhttp3.OkHttpClient;
-import okhttp3.Request;
-import okhttp3.RequestBody;
-import okhttp3.Response;
-import use_case.change_password.ChangePasswordUserDataAccessInterface;
-import use_case.login.LoginUserDataAccessInterface;
-import use_case.logout.LogoutUserDataAccessInterface;
-import use_case.signup.SignupUserDataAccessInterface;
-
-/**
- * The DAO for user data.
- */
-public class DBUserDataAccessObject implements SignupUserDataAccessInterface,
- LoginUserDataAccessInterface,
- ChangePasswordUserDataAccessInterface,
- LogoutUserDataAccessInterface {
- private static final int SUCCESS_CODE = 200;
- private static final String CONTENT_TYPE_LABEL = "Content-Type";
- private static final String CONTENT_TYPE_JSON = "application/json";
- private static final String STATUS_CODE_LABEL = "status_code";
- private static final String USERNAME = "username";
- private static final String PASSWORD = "password";
- private static final String MESSAGE = "message";
- private final UserFactory userFactory;
-
- public DBUserDataAccessObject(UserFactory userFactory) {
- this.userFactory = userFactory;
- // No need to do anything to reinitialize a user list! The data is the cloud that may be miles away.
- }
-
- @Override
- public User get(String username) {
- // Make an API call to get the user object.
- final OkHttpClient client = new OkHttpClient().newBuilder().build();
- final Request request = new Request.Builder()
- .url(String.format("http://vm003.teach.cs.toronto.edu:20112/user?username=%s", username))
- .addHeader("Content-Type", CONTENT_TYPE_JSON)
- .build();
- try {
- final Response response = client.newCall(request).execute();
-
- final JSONObject responseBody = new JSONObject(response.body().string());
-
- if (responseBody.getInt(STATUS_CODE_LABEL) == SUCCESS_CODE) {
- final JSONObject userJSONObject = responseBody.getJSONObject("user");
- final String name = userJSONObject.getString(USERNAME);
- final String password = userJSONObject.getString(PASSWORD);
-
- return userFactory.create(name, password);
- }
- else {
- throw new RuntimeException(responseBody.getString(MESSAGE));
- }
- }
- catch (IOException | JSONException ex) {
- throw new RuntimeException(ex);
- }
- }
-
- @Override
- public void setCurrentUsername(String name) {
- // this isn't implemented for the lab
- }
-
- @Override
- public boolean existsByName(String username) {
- final OkHttpClient client = new OkHttpClient().newBuilder()
- .build();
- final Request request = new Request.Builder()
- .url(String.format("http://vm003.teach.cs.toronto.edu:20112/checkIfUserExists?username=%s", username))
- .addHeader(CONTENT_TYPE_LABEL, CONTENT_TYPE_JSON)
- .build();
- try {
- final Response response = client.newCall(request).execute();
-
- final JSONObject responseBody = new JSONObject(response.body().string());
-
- return responseBody.getInt(STATUS_CODE_LABEL) == SUCCESS_CODE;
- }
- catch (IOException | JSONException ex) {
- throw new RuntimeException(ex);
- }
- }
-
- @Override
- public void save(User user) {
- final OkHttpClient client = new OkHttpClient().newBuilder()
- .build();
-
- // POST METHOD
- final MediaType mediaType = MediaType.parse(CONTENT_TYPE_JSON);
- final JSONObject requestBody = new JSONObject();
- requestBody.put(USERNAME, user.getName());
- requestBody.put(PASSWORD, user.getPassword());
- final RequestBody body = RequestBody.create(requestBody.toString(), mediaType);
- final Request request = new Request.Builder()
- .url("http://vm003.teach.cs.toronto.edu:20112/user")
- .method("POST", body)
- .addHeader(CONTENT_TYPE_LABEL, CONTENT_TYPE_JSON)
- .build();
- try {
- final Response response = client.newCall(request).execute();
-
- final JSONObject responseBody = new JSONObject(response.body().string());
-
- if (responseBody.getInt(STATUS_CODE_LABEL) == SUCCESS_CODE) {
- // success!
- }
- else {
- throw new RuntimeException(responseBody.getString(MESSAGE));
- }
- }
- catch (IOException | JSONException ex) {
- throw new RuntimeException(ex);
- }
- }
-
- @Override
- public void changePassword(User user) {
- final OkHttpClient client = new OkHttpClient().newBuilder()
- .build();
-
- // POST METHOD
- final MediaType mediaType = MediaType.parse(CONTENT_TYPE_JSON);
- final JSONObject requestBody = new JSONObject();
- requestBody.put(USERNAME, user.getName());
- requestBody.put(PASSWORD, user.getPassword());
- final RequestBody body = RequestBody.create(requestBody.toString(), mediaType);
- final Request request = new Request.Builder()
- .url("http://vm003.teach.cs.toronto.edu:20112/user")
- .method("PUT", body)
- .addHeader(CONTENT_TYPE_LABEL, CONTENT_TYPE_JSON)
- .build();
- try {
- final Response response = client.newCall(request).execute();
-
- final JSONObject responseBody = new JSONObject(response.body().string());
-
- if (responseBody.getInt(STATUS_CODE_LABEL) == SUCCESS_CODE) {
- // success!
- }
- else {
- throw new RuntimeException(responseBody.getString(MESSAGE));
- }
- }
- catch (IOException | JSONException ex) {
- throw new RuntimeException(ex);
- }
- }
-
- @Override
- public String getCurrentUsername() {
- return null;
- }
-}
diff --git a/src/main/java/data_access/FileUserDataAccessObject.java b/src/main/java/data_access/FileUserDataAccessObject.java
deleted file mode 100644
index d301a3241..000000000
--- a/src/main/java/data_access/FileUserDataAccessObject.java
+++ /dev/null
@@ -1,117 +0,0 @@
-package data_access;
-
-import java.io.BufferedReader;
-import java.io.BufferedWriter;
-import java.io.File;
-import java.io.FileReader;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.Map;
-
-import entity.User;
-import entity.UserFactory;
-import use_case.change_password.ChangePasswordUserDataAccessInterface;
-import use_case.login.LoginUserDataAccessInterface;
-import use_case.signup.SignupUserDataAccessInterface;
-
-/**
- * DAO for user data implemented using a File to persist the data.
- */
-public class FileUserDataAccessObject implements SignupUserDataAccessInterface,
- LoginUserDataAccessInterface,
- ChangePasswordUserDataAccessInterface {
-
- private static final String HEADER = "username,password";
-
- private final File csvFile;
- private final Map headers = new LinkedHashMap<>();
- private final Map accounts = new HashMap<>();
- private String currentUsername;
-
- public FileUserDataAccessObject(String csvPath, UserFactory userFactory) throws IOException {
-
- csvFile = new File(csvPath);
- headers.put("username", 0);
- headers.put("password", 1);
-
- if (csvFile.length() == 0) {
- save();
- }
- else {
-
- try (BufferedReader reader = new BufferedReader(new FileReader(csvFile))) {
- final String header = reader.readLine();
-
- if (!header.equals(HEADER)) {
- throw new RuntimeException(String.format("header should be%n: %s%but was:%n%s", HEADER, header));
- }
-
- String row;
- while ((row = reader.readLine()) != null) {
- final String[] col = row.split(",");
- final String username = String.valueOf(col[headers.get("username")]);
- final String password = String.valueOf(col[headers.get("password")]);
- final User user = userFactory.create(username, password);
- accounts.put(username, user);
- }
- }
- }
- }
-
- private void save() {
- final BufferedWriter writer;
- try {
- writer = new BufferedWriter(new FileWriter(csvFile));
- writer.write(String.join(",", headers.keySet()));
- writer.newLine();
-
- for (User user : accounts.values()) {
- final String line = String.format("%s,%s",
- user.getName(), user.getPassword());
- writer.write(line);
- writer.newLine();
- }
-
- writer.close();
-
- }
- catch (IOException ex) {
- throw new RuntimeException(ex);
- }
- }
-
- @Override
- public void save(User user) {
- accounts.put(user.getName(), user);
- this.save();
- }
-
- @Override
- public User get(String username) {
- return accounts.get(username);
- }
-
- @Override
- public void setCurrentUsername(String name) {
- this.currentUsername = name;
- }
-
- @Override
- public String getCurrentUsername() {
- return this.currentUsername;
- }
-
- @Override
- public boolean existsByName(String identifier) {
- return accounts.containsKey(identifier);
- }
-
- @Override
- public void changePassword(User user) {
- // Replace the User object in the map
- accounts.put(user.getName(), user);
- save();
- }
-}
diff --git a/src/main/java/data_access/InMemoryUserDataAccessObject.java b/src/main/java/data_access/InMemoryUserDataAccessObject.java
deleted file mode 100644
index 71f00862c..000000000
--- a/src/main/java/data_access/InMemoryUserDataAccessObject.java
+++ /dev/null
@@ -1,55 +0,0 @@
-package data_access;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import entity.User;
-import use_case.change_password.ChangePasswordUserDataAccessInterface;
-import use_case.login.LoginUserDataAccessInterface;
-import use_case.logout.LogoutUserDataAccessInterface;
-import use_case.signup.SignupUserDataAccessInterface;
-
-/**
- * In-memory implementation of the DAO for storing user data. This implementation does
- * NOT persist data between runs of the program.
- */
-public class InMemoryUserDataAccessObject implements SignupUserDataAccessInterface,
- LoginUserDataAccessInterface,
- ChangePasswordUserDataAccessInterface,
- LogoutUserDataAccessInterface {
-
- private final Map users = new HashMap<>();
-
- private String currentUsername;
-
- @Override
- public boolean existsByName(String identifier) {
- return users.containsKey(identifier);
- }
-
- @Override
- public void save(User user) {
- users.put(user.getName(), user);
- }
-
- @Override
- public User get(String username) {
- return users.get(username);
- }
-
- @Override
- public void changePassword(User user) {
- // Replace the old entry with the new password
- users.put(user.getName(), user);
- }
-
- @Override
- public void setCurrentUsername(String name) {
- this.currentUsername = name;
- }
-
- @Override
- public String getCurrentUsername() {
- return this.currentUsername;
- }
-}
diff --git a/src/main/java/entity/CommonUserFactory.java b/src/main/java/entity/CommonUserFactory.java
deleted file mode 100644
index ebede69e3..000000000
--- a/src/main/java/entity/CommonUserFactory.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package entity;
-
-/**
- * Factory for creating CommonUser objects.
- */
-public class CommonUserFactory implements UserFactory {
-
- @Override
- public User create(String name, String password) {
- return new CommonUser(name, password);
- }
-}
diff --git a/src/main/java/entity/UserFactory.java b/src/main/java/entity/UserFactory.java
deleted file mode 100644
index c7a508708..000000000
--- a/src/main/java/entity/UserFactory.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package entity;
-
-/**
- * Factory for creating users.
- */
-public interface UserFactory {
- /**
- * Creates a new User.
- * @param name the name of the new user
- * @param password the password of the new user
- * @return the new user
- */
- User create(String name, String password);
-
-}
diff --git a/src/main/java/interface_adapter/login/LoginPresenter.java b/src/main/java/interface_adapter/login/LoginPresenter.java
deleted file mode 100644
index 66560d51a..000000000
--- a/src/main/java/interface_adapter/login/LoginPresenter.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package interface_adapter.login;
-
-import interface_adapter.ViewManagerModel;
-import interface_adapter.change_password.LoggedInState;
-import interface_adapter.change_password.LoggedInViewModel;
-import use_case.login.LoginOutputBoundary;
-import use_case.login.LoginOutputData;
-
-/**
- * The Presenter for the Login Use Case.
- */
-public class LoginPresenter implements LoginOutputBoundary {
-
- private final LoginViewModel loginViewModel;
- private final LoggedInViewModel loggedInViewModel;
- private final ViewManagerModel viewManagerModel;
-
- public LoginPresenter(ViewManagerModel viewManagerModel,
- LoggedInViewModel loggedInViewModel,
- LoginViewModel loginViewModel) {
- this.viewManagerModel = viewManagerModel;
- this.loggedInViewModel = loggedInViewModel;
- this.loginViewModel = loginViewModel;
- }
-
- @Override
- public void prepareSuccessView(LoginOutputData response) {
- // On success, switch to the logged in view.
-
- final LoggedInState loggedInState = loggedInViewModel.getState();
- loggedInState.setUsername(response.getUsername());
- this.loggedInViewModel.setState(loggedInState);
- this.loggedInViewModel.firePropertyChanged();
-
- this.viewManagerModel.setState(loggedInViewModel.getViewName());
- this.viewManagerModel.firePropertyChanged();
- }
-
- @Override
- public void prepareFailView(String error) {
- final LoginState loginState = loginViewModel.getState();
- loginState.setLoginError(error);
- loginViewModel.firePropertyChanged();
- }
-}
diff --git a/src/main/java/interface_adapter/logout/LogoutController.java b/src/main/java/interface_adapter/logout/LogoutController.java
deleted file mode 100644
index e184a3bba..000000000
--- a/src/main/java/interface_adapter/logout/LogoutController.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package interface_adapter.logout;
-
-import use_case.logout.LogoutInputBoundary;
-
-/**
- * The controller for the Logout Use Case.
- */
-public class LogoutController {
-
- private LogoutInputBoundary logoutUseCaseInteractor;
-
- public LogoutController(LogoutInputBoundary logoutUseCaseInteractor) {
- // TODO: Save the interactor in the instance variable.
- }
-
- /**
- * Executes the Logout Use Case.
- * @param username the username of the user logging in
- */
- public void execute(String username) {
- // TODO: run the use case interactor for the logout use case
- // 1. instantiate the `LogoutInputData`, which should contain the username.
- // 2. tell the Interactor to execute.
- }
-}
diff --git a/src/main/java/interface_adapter/logout/LogoutPresenter.java b/src/main/java/interface_adapter/logout/LogoutPresenter.java
deleted file mode 100644
index 78ef306a1..000000000
--- a/src/main/java/interface_adapter/logout/LogoutPresenter.java
+++ /dev/null
@@ -1,54 +0,0 @@
-package interface_adapter.logout;
-
-import interface_adapter.ViewManagerModel;
-import interface_adapter.change_password.LoggedInViewModel;
-import interface_adapter.login.LoginViewModel;
-import use_case.logout.LogoutOutputBoundary;
-import use_case.logout.LogoutOutputData;
-
-/**
- * The Presenter for the Logout Use Case.
- */
-public class LogoutPresenter implements LogoutOutputBoundary {
-
- private LoggedInViewModel loggedInViewModel;
- private ViewManagerModel viewManagerModel;
- private LoginViewModel loginViewModel;
-
- public LogoutPresenter(ViewManagerModel viewManagerModel,
- LoggedInViewModel loggedInViewModel,
- LoginViewModel loginViewModel) {
- // TODO: assign to the three instance variables.
- }
-
- @Override
- public void prepareSuccessView(LogoutOutputData response) {
- // We need to switch to the login view, which should have
- // an empty username and password.
-
- // We also need to set the username in the LoggedInState to
- // the empty string.
-
- // TODO: have prepareSuccessView update the LoggedInState
- // 1. get the LoggedInState out of the appropriate View Model,
- // 2. set the username in the state to the empty string
- // 3. set the state in the LoggedInViewModel to the updated state
- // 4. firePropertyChanged so that the View that is listening is updated.
-
- // TODO: have prepareSuccessView update the LoginState
- // 5. get the LoginState out of the appropriate View Model,
- // 6. set the username and password in the state to the empty string
- // 7. set the state in the LoginViewModel to the updated state
- // 8. firePropertyChanged so that the View that is listening is updated.
-
- // This code tells the View Manager to switch to the LoginView.
- this.viewManagerModel.setState(loginViewModel.getViewName());
- this.viewManagerModel.firePropertyChanged();
- }
-
- @Override
- public void prepareFailView(String error) {
- // No need to add code here. We'll assume that logout can't fail.
- // Thought question: is this a reasonable assumption?
- }
-}
diff --git a/src/main/java/interface_adapter/signup/SignupPresenter.java b/src/main/java/interface_adapter/signup/SignupPresenter.java
deleted file mode 100644
index 9b654077e..000000000
--- a/src/main/java/interface_adapter/signup/SignupPresenter.java
+++ /dev/null
@@ -1,50 +0,0 @@
-package interface_adapter.signup;
-
-import interface_adapter.ViewManagerModel;
-import interface_adapter.login.LoginState;
-import interface_adapter.login.LoginViewModel;
-import use_case.signup.SignupOutputBoundary;
-import use_case.signup.SignupOutputData;
-
-/**
- * The Presenter for the Signup Use Case.
- */
-public class SignupPresenter implements SignupOutputBoundary {
-
- private final SignupViewModel signupViewModel;
- private final LoginViewModel loginViewModel;
- private final ViewManagerModel viewManagerModel;
-
- public SignupPresenter(ViewManagerModel viewManagerModel,
- SignupViewModel signupViewModel,
- LoginViewModel loginViewModel) {
- this.viewManagerModel = viewManagerModel;
- this.signupViewModel = signupViewModel;
- this.loginViewModel = loginViewModel;
- }
-
- @Override
- public void prepareSuccessView(SignupOutputData response) {
- // On success, switch to the login view.
- final LoginState loginState = loginViewModel.getState();
- loginState.setUsername(response.getUsername());
- this.loginViewModel.setState(loginState);
- loginViewModel.firePropertyChanged();
-
- viewManagerModel.setState(loginViewModel.getViewName());
- viewManagerModel.firePropertyChanged();
- }
-
- @Override
- public void prepareFailView(String error) {
- final SignupState signupState = signupViewModel.getState();
- signupState.setUsernameError(error);
- signupViewModel.firePropertyChanged();
- }
-
- @Override
- public void switchToLoginView() {
- viewManagerModel.setState(loginViewModel.getViewName());
- viewManagerModel.firePropertyChanged();
- }
-}
diff --git a/src/main/java/interface_adapter/signup/SignupViewModel.java b/src/main/java/interface_adapter/signup/SignupViewModel.java
deleted file mode 100644
index 01f0086bc..000000000
--- a/src/main/java/interface_adapter/signup/SignupViewModel.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package interface_adapter.signup;
-
-import interface_adapter.ViewModel;
-
-/**
- * The ViewModel for the Signup View.
- */
-public class SignupViewModel extends ViewModel {
-
- public static final String TITLE_LABEL = "Sign Up View";
- public static final String USERNAME_LABEL = "Choose username";
- public static final String PASSWORD_LABEL = "Choose password";
- public static final String REPEAT_PASSWORD_LABEL = "Enter password again";
-
- public static final String SIGNUP_BUTTON_LABEL = "Sign up";
- public static final String CANCEL_BUTTON_LABEL = "Cancel";
-
- public static final String TO_LOGIN_BUTTON_LABEL = "Go to Login";
-
- public SignupViewModel() {
- super("sign up");
- setState(new SignupState());
- }
-
-}
diff --git a/src/main/java/osiris/PlaidIntegrationApplication.java b/src/main/java/osiris/PlaidIntegrationApplication.java
new file mode 100644
index 000000000..3420c783d
--- /dev/null
+++ b/src/main/java/osiris/PlaidIntegrationApplication.java
@@ -0,0 +1,20 @@
+package osiris;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+/**
+ * The main class for the Plaid Integration application.
+ */
+@SpringBootApplication
+public class PlaidIntegrationApplication {
+
+ /**
+ * The main method of the application.
+ *
+ * @param args unused arguments
+ */
+ public static void main(String[] args) {
+ SpringApplication.run(PlaidIntegrationApplication.class, args);
+ }
+}
diff --git a/src/main/java/osiris/app/AppBuilder.java b/src/main/java/osiris/app/AppBuilder.java
new file mode 100644
index 000000000..7f65c3c62
--- /dev/null
+++ b/src/main/java/osiris/app/AppBuilder.java
@@ -0,0 +1,360 @@
+package osiris.app;
+
+import java.awt.CardLayout;
+
+import javax.swing.JFrame;
+import javax.swing.JPanel;
+import javax.swing.WindowConstants;
+import java.awt.Toolkit;
+import java.awt.Dimension;
+
+import osiris.data_access.DBUserDataAccessObject;
+import osiris.data_access.PlaidDataAccessObject;
+import osiris.entity.CommonUserFactory;
+import osiris.entity.UserFactory;
+import osiris.data_access.EmailServiceImpl;
+import osiris.interface_adapter.ViewManagerModel;
+import osiris.interface_adapter.grabtransaction.GrabTransactionController;
+import osiris.interface_adapter.login.LoginController;
+import osiris.interface_adapter.login.LoginPresenter;
+import osiris.interface_adapter.login.LoginViewModel;
+import osiris.interface_adapter.plaid.PlaidController;
+import osiris.interface_adapter.signup.SignupController;
+import osiris.interface_adapter.signup.SignupPresenter;
+import osiris.interface_adapter.signup.SignupViewModel;
+import osiris.interface_adapter.viewexpenses.ViewExpensesController;
+import osiris.interface_adapter.viewexpenses.ViewExpensesPresenter;
+import osiris.interface_adapter.viewexpenses.ViewExpensesViewModel;
+import osiris.interface_adapter.welcome.WelcomeController;
+import osiris.interface_adapter.welcome.WelcomePresenter;
+import osiris.interface_adapter.welcome.WelcomeViewModel;
+import osiris.interface_adapter.verify.VerifyController;
+import osiris.interface_adapter.verify.VerifyPresenter;
+import osiris.interface_adapter.verify.VerifyViewModel;
+import osiris.interface_adapter.dashboard.DashboardController;
+import osiris.interface_adapter.dashboard.DashboardPresenter;
+import osiris.interface_adapter.dashboard.DashboardViewModel;
+import osiris.interface_adapter.budget.BudgetController;
+import osiris.interface_adapter.budget.BudgetPresenter;
+import osiris.interface_adapter.budget.BudgetViewModel;
+import osiris.use_case.grabtransactions.GrabTransactionsInputBoundary;
+import osiris.use_case.grabtransactions.GrabTransactionsInputData;
+import osiris.use_case.grabtransactions.GrabTransactionsInteractor;
+import osiris.use_case.login.LoginInputBoundary;
+import osiris.use_case.login.LoginInteractor;
+import osiris.use_case.login.LoginOutputBoundary;
+import osiris.use_case.plaid.PlaidDataBaseUserAccessObjectInterface;
+import osiris.use_case.plaid.PlaidInputBoundary;
+import osiris.use_case.plaid.PlaidInteractor;
+import osiris.use_case.verify.VerifyInputBoundary;
+import osiris.use_case.verify.VerifyInteractor;
+import osiris.use_case.verify.VerifyOutputBoundary;
+import osiris.use_case.signup.SignupInputBoundary;
+import osiris.use_case.signup.SignupInteractor;
+import osiris.use_case.signup.SignupOutputBoundary;
+import osiris.use_case.viewexpenses.ViewExpensesInputBoundary;
+import osiris.use_case.viewexpenses.ViewExpensesInteractor;
+import osiris.use_case.viewexpenses.ViewExpensesOutputBoundary;
+import osiris.use_case.welcome.WelcomeInputBoundary;
+import osiris.use_case.welcome.WelcomeInteractor;
+import osiris.use_case.welcome.WelcomeOutputBoundary;
+import osiris.use_case.dashboard.DashboardInputBoundary;
+import osiris.use_case.dashboard.DashboardInteractor;
+import osiris.use_case.dashboard.DashboardOutputBoundary;
+import osiris.use_case.budget.BudgetInputBoundary;
+import osiris.use_case.budget.BudgetInteractor;
+import osiris.use_case.budget.BudgetOutputBoundary;
+import osiris.view.*;
+
+
+/**
+ * The AppBuilder class is responsible for putting together the pieces of
+ * our CA architecture; piece by piece.
+ *
+ * This is done by adding each View and then adding related Use Cases.
+ */
+// Checkstyle note: you can ignore the "Class Data Abstraction Coupling"
+// and the "Class Fan-Out Complexity" issues for this lab; we encourage
+// your team to think about ways to refactor the code to resolve these
+// if your team decides to work with this as your starter code
+// for your final project this term.
+public class AppBuilder {
+ private final JPanel cardPanel = new JPanel();
+ private final CardLayout cardLayout = new CardLayout();
+ // thought question: is the hard dependency below a problem?
+ private final UserFactory userFactory = new CommonUserFactory();
+ private final ViewManagerModel viewManagerModel = new ViewManagerModel();
+ private final ViewManager viewManager = new ViewManager(cardPanel, cardLayout, viewManagerModel);
+
+ // thought question: is the hard dependency below a problem?
+ private final DBUserDataAccessObject userDataAccessObject = new DBUserDataAccessObject(userFactory);
+ private final PlaidDataAccessObject plaidDataAccessObject = new PlaidDataAccessObject();
+
+ private final EmailServiceImpl emailService = new EmailServiceImpl();
+
+ private SignupView signupView;
+ private SignupViewModel signupViewModel;
+ private LoginViewModel loginViewModel;
+ private LoginView loginView;
+ private WelcomeView welcomeView;
+ private WelcomeViewModel welcomeViewModel;
+ private VerifyViewModel verifyViewModel;
+ private VerifyView verifyView;
+ private DashboardView dashboardView;
+ private DashboardViewModel dashboardViewModel;
+ private ViewExpenses viewExpenses;
+ private ViewExpensesViewModel viewExpensesViewModel;
+ private BudgetView budgetView;
+ private BudgetViewModel budgetViewModel;
+
+ public AppBuilder() {
+ cardPanel.setLayout(cardLayout);
+ cardPanel.setPreferredSize(new Dimension(850, 550));
+ }
+
+ /**
+ * Adds the Signup View to the application.
+ * @return this builder
+ */
+ public AppBuilder addSignupView() {
+ signupViewModel = new SignupViewModel();
+ signupView = new SignupView(signupViewModel);
+ cardPanel.add(signupView, signupView.getViewName());
+ return this;
+ }
+
+ /**
+ * Adds the ViewExpenses View to the application.
+ * @return this builder
+ */
+ public AppBuilder addViewExpensesView() {
+ viewExpensesViewModel = new ViewExpensesViewModel();
+ viewExpenses = new ViewExpenses(viewExpensesViewModel);
+ cardPanel.add(viewExpenses, viewExpenses.getViewName());
+ return this;
+ }
+
+ /**
+ * Adds the Budget View to the application.
+ * @return this builder
+ */
+ public AppBuilder addBudgetView() {
+ budgetViewModel = new BudgetViewModel();
+ budgetView = new BudgetView(budgetViewModel);
+ cardPanel.add(budgetView, budgetView.getViewName());
+ return this;
+ }
+
+ public AppBuilder addWelcomeView() {
+ welcomeViewModel = new WelcomeViewModel();
+ welcomeView = new WelcomeView(welcomeViewModel);
+ cardPanel.add(welcomeView, welcomeView.getViewName());
+ return this;
+ }
+
+ /**
+ * Adds the Login View to the application.
+ * @return this builder
+ */
+ public AppBuilder addDashboardView() {
+ dashboardViewModel = new DashboardViewModel();
+ dashboardView = new DashboardView(dashboardViewModel);
+ cardPanel.add(dashboardView, dashboardView.getViewName());
+ return this;
+ }
+
+ /**
+ * Adds the welcome Use Case to the application.
+ * @return this builder
+ */
+ public AppBuilder addDashboardUseCase() {
+ final DashboardOutputBoundary dashboardOutputBoundary = new DashboardPresenter(viewManagerModel,
+ dashboardViewModel, viewExpensesViewModel, budgetViewModel);
+ final DashboardInputBoundary userDashboardInteractor = new DashboardInteractor(dashboardOutputBoundary,
+ userFactory);
+
+ final DashboardController controller = new DashboardController(userDashboardInteractor);
+ dashboardView.setDashboardController(controller);
+ return this;
+ }
+ /**
+ * Adds the welcome Use Case to the application.
+ * @return this builder
+ */
+ public AppBuilder addWelcomeUseCase() {
+ final WelcomeOutputBoundary welcomeOutputBoundary = new WelcomePresenter(viewManagerModel,
+ welcomeViewModel, loginViewModel, signupViewModel);
+ final WelcomeInputBoundary userWelcomeInteractor = new WelcomeInteractor(welcomeOutputBoundary);
+
+ final WelcomeController controller = new WelcomeController(userWelcomeInteractor);
+ welcomeView.setWelcomeController(controller);
+ return this;
+ }
+
+ /**
+ * Adds the Login View to the application.
+ * @return this builder
+ */
+ public AppBuilder addLoginView() {
+ loginViewModel = new LoginViewModel();
+ loginView = new LoginView(loginViewModel);
+ cardPanel.add(loginView, loginView.getViewName());
+ return this;
+ }
+
+ /**
+ * Adds the Verify View to the application.
+ * @return this builder
+ */
+
+ public AppBuilder addVerifyView() {
+ verifyViewModel = new VerifyViewModel();
+ verifyView = new VerifyView(verifyViewModel);
+ cardPanel.add(verifyView, verifyView.getViewName());
+ return this;
+ }
+
+ /**
+ * Adds the Signup Use Case to the application.
+ * @return this builder
+ */
+ public AppBuilder addSignupUseCase() {
+ final SignupOutputBoundary signupOutputBoundary = new SignupPresenter(viewManagerModel,
+ signupViewModel, verifyViewModel, welcomeViewModel, dashboardViewModel);
+ final SignupInputBoundary userSignupInteractor = new SignupInteractor(
+ userDataAccessObject, signupOutputBoundary, userFactory, emailService);
+
+ final SignupController controller = new SignupController(userSignupInteractor);
+ signupView.setSignupController(controller);
+ return this;
+ }
+
+ /**
+ * Adds the ViewExpenses View to the application.
+ * @return this builder
+ */
+ public AppBuilder addViewExpensesUseCase() {
+ final ViewExpensesOutputBoundary viewExpensesOutputBoundary = new ViewExpensesPresenter(viewExpensesViewModel,
+ viewManagerModel);
+
+ final ViewExpensesInputBoundary userViewExpensesInteractor = new ViewExpensesInteractor(
+ viewExpensesOutputBoundary);
+
+ final GrabTransactionsInputBoundary userViewExpensesGrab = new GrabTransactionsInteractor(userDataAccessObject,
+ plaidDataAccessObject);
+
+ final ViewExpensesController controller = new ViewExpensesController(userViewExpensesInteractor);
+ final GrabTransactionController grabTransactionController = new GrabTransactionController(userViewExpensesGrab);
+
+ viewExpenses.setViewExpensesController(controller);
+ viewExpenses.setGrabTransactionController(grabTransactionController);
+
+ return this;
+ }
+
+ /**
+ * Adds the Budget View to the application.
+ * @return this builder
+ */
+ public AppBuilder addBudgetUseCase() {
+ final BudgetOutputBoundary budgetOutputBoundary = new BudgetPresenter(budgetViewModel,
+ viewManagerModel);
+
+ final BudgetInputBoundary userBudgetInteractor = new BudgetInteractor(
+ budgetOutputBoundary);
+
+ final GrabTransactionsInputBoundary userBudgetGrab = new GrabTransactionsInteractor(userDataAccessObject,
+ plaidDataAccessObject);
+
+ final BudgetController controller = new BudgetController(userBudgetInteractor);
+ final GrabTransactionController grabTransactionController = new GrabTransactionController(userBudgetGrab);
+
+ budgetView.setBudgetController(controller);
+ budgetView.setGrabTransactionController(grabTransactionController);
+
+ return this;
+ }
+
+ /**
+ * Adds the Login Use Case to the application.
+ * @return this builder
+ */
+ public AppBuilder addLoginUseCase() {
+ final LoginOutputBoundary loginOutputBoundary = new LoginPresenter(viewManagerModel,
+ loginViewModel, welcomeViewModel, verifyViewModel, dashboardViewModel);
+ final LoginInputBoundary loginInteractor = new LoginInteractor(
+ userDataAccessObject, loginOutputBoundary, userFactory, plaidDataAccessObject, userDataAccessObject);
+ final LoginController loginController = new LoginController(loginInteractor);
+ loginView.setLoginController(loginController);
+ return this;
+ }
+
+ /**
+ * Adds the Change Password Use Case to the application.
+ * @return this builder
+ */
+// public AppBuilder addChangePasswordUseCase() {
+// final ChangePasswordOutputBoundary changePasswordOutputBoundary =
+// new ChangePasswordPresenter(loggedInViewModel);
+//
+// final ChangePasswordInputBoundary changePasswordInteractor =
+// new ChangePasswordInteractor(userDataAccessObject, changePasswordOutputBoundary, userFactory);
+//
+// final ChangePasswordController changePasswordController =
+// new ChangePasswordController(changePasswordInteractor);
+// loggedInView.setChangePasswordController(changePasswordController);
+// return this;
+// }
+
+ public AppBuilder addVerifyUseCase() {
+ final VerifyOutputBoundary verifyOutputBoundary =
+ new VerifyPresenter(verifyViewModel, signupViewModel, viewManagerModel);
+
+ final VerifyInputBoundary verifyInteractor =
+ new VerifyInteractor(verifyOutputBoundary, emailService, userDataAccessObject);
+
+ final VerifyController verifyController =
+ new VerifyController(verifyInteractor);
+ verifyView.setVerifyController(verifyController);
+ return this;
+
+ }
+
+ /**
+ * Adds the Logout Use Case to the application.
+ * @return this builder
+ */
+// public AppBuilder addLogoutUseCase() {
+// final LogoutOutputBoundary logoutOutputBoundary = new LogoutPresenter(viewManagerModel,
+// loggedInViewModel, loginViewModel);
+//
+// final LogoutInputBoundary logoutInteractor =
+// new LogoutInteractor(userDataAccessObject, logoutOutputBoundary);
+//
+// final LogoutController logoutController = new LogoutController(logoutInteractor);
+// loggedInView.setLogoutController(logoutController);
+// return this;
+// }
+
+
+ /**
+ * Creates the JFrame for the application and initially sets the SignupView to be displayed.
+ * @return the application
+ */
+ public JFrame build() {
+ final JFrame application = new JFrame("Osiris");
+ application.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
+ application.setSize(850, 550);
+ final Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
+ final Dimension frameSize = application.getSize();
+ final int x = (screenSize.width - frameSize.width) / 2;
+ final int y = (screenSize.height - frameSize.height) / 2;
+ application.setLocation(x, y);
+ application.add(cardPanel);
+
+ viewManagerModel.setState(welcomeView.getViewName());
+ viewManagerModel.firePropertyChanged();
+
+ return application;
+ }
+}
diff --git a/src/main/java/osiris/app/Main.java b/src/main/java/osiris/app/Main.java
new file mode 100644
index 000000000..c317d94fb
--- /dev/null
+++ b/src/main/java/osiris/app/Main.java
@@ -0,0 +1,40 @@
+package osiris.app;
+
+import javax.swing.JFrame;
+
+/**
+ * The Main class of our application.
+ */
+public class Main {
+ /**
+ * Builds and runs the CA architecture of the application.
+ * @param args unused arguments
+ */
+ public static void main(String[] args) {
+ final AppBuilder appBuilder = new AppBuilder();
+
+ final JFrame application = appBuilder
+ .addLoginView()
+ .addSignupView()
+// .addVerifyView()
+// .addVerifyUseCase()
+ .addWelcomeView()
+ .addDashboardView()
+ .addSignupUseCase()
+ .addWelcomeUseCase()
+ .addLoginUseCase()
+ .addBudgetView()
+ .addBudgetUseCase()
+
+ .addDashboardUseCase()
+// .addVerifyUseCase()
+// .addChangePasswordUseCase()
+// .addLogoutUseCase()
+ .addViewExpensesView()
+ .addViewExpensesUseCase()
+ .build();
+
+ application.pack();
+ application.setVisible(true);
+ }
+}
diff --git a/src/main/java/osiris/data_access/DBUserDataAccessObject.java b/src/main/java/osiris/data_access/DBUserDataAccessObject.java
new file mode 100644
index 000000000..1673ad7d6
--- /dev/null
+++ b/src/main/java/osiris/data_access/DBUserDataAccessObject.java
@@ -0,0 +1,205 @@
+package osiris.data_access;
+
+import java.io.IOException;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.springframework.stereotype.Component;
+
+import okhttp3.MediaType;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.RequestBody;
+import okhttp3.Response;
+import osiris.entity.User;
+import osiris.entity.UserFactory;
+import osiris.use_case.change_password.ChangePasswordUserDataAccessInterface;
+import osiris.use_case.grabtransactions.GrabTransactionUserDataAccessInterface;
+import osiris.use_case.login.LoginUserDataAccessInterface;
+import osiris.use_case.logout.LogoutUserDataAccessInterface;
+import osiris.use_case.plaid.PlaidDataBaseUserAccessObjectInterface;
+import osiris.use_case.signup.SignupUserDataAccessInterface;
+
+/**
+ * The Data Access Object (DAO) for user data.
+ */
+@Component
+public class DBUserDataAccessObject implements SignupUserDataAccessInterface,
+ LoginUserDataAccessInterface,
+ ChangePasswordUserDataAccessInterface,
+ LogoutUserDataAccessInterface,
+ PlaidDataBaseUserAccessObjectInterface,
+ GrabTransactionUserDataAccessInterface {
+
+ private static final int SUCCESS_CODE = 200;
+ private static final int ONE = 1;
+ private static final int TWO = 2;
+ private static final int THREE = 3;
+ private static final String CONTENT_TYPE_LABEL = "Content-Type";
+ private static final String CONTENT_TYPE_JSON = "application/json";
+ private static final String STATUS_CODE_LABEL = "status_code";
+ private static final String DB_URL = "jdbc:mysql://localhost:3306/schema_name";
+ private static final String DB_USER = "root";
+ private static final String DB_PASSWORD = "Divnoor123";
+ private static final String USERNAME = "username";
+ private static final String PASSWORD = "passwords";
+ private static final String MESSAGE = "message";
+ private static final String ACCESS_CODE = "accessCode";
+ private static final String COM_MYSQL_CJ_JDBC_DRIVER = "com.mysql.cj.jdbc.Driver";
+
+ private final UserFactory userFactory;
+ private String currentEmail;
+
+ /**
+ * Constructs a DBUserDataAccessObject with the specified UserFactory.
+ *
+ * @param userFactory The UserFactory to create user instances.
+ */
+ public DBUserDataAccessObject(UserFactory userFactory) {
+ this.userFactory = userFactory;
+ }
+
+ @Override
+ public User get(String username) {
+ final String sql = "SELECT username, passwords, accessCode FROM users WHERE username = ?";
+ try {
+ Class.forName(COM_MYSQL_CJ_JDBC_DRIVER);
+
+ try (Connection conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD);
+ PreparedStatement pstmt = conn.prepareStatement(sql)) {
+
+ pstmt.setString(ONE, username);
+
+ try (ResultSet rs = pstmt.executeQuery()) {
+ if (rs.next()) {
+ final String name = rs.getString(USERNAME);
+ final String passwords = rs.getString(PASSWORD);
+ final String accessCode = rs.getString(ACCESS_CODE);
+
+ return userFactory.create(name, passwords, accessCode);
+ }
+ }
+ }
+ }
+ catch (SQLException | ClassNotFoundException ex) {
+ throw new RuntimeException("Error retrieving user: " + ex.getMessage(), ex);
+ }
+ return null;
+ }
+
+ @Override
+ public void setCurrentEmail(String name) {
+ this.currentEmail = name;
+ }
+
+ @Override
+ public boolean existsByName(String username) {
+ final String sql = "SELECT ONE FROM users WHERE username = ?";
+ try {
+ Class.forName(COM_MYSQL_CJ_JDBC_DRIVER);
+ try (Connection conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD);
+ PreparedStatement pstmt = conn.prepareStatement(sql)) {
+ pstmt.setString(ONE, username);
+ try (ResultSet rs = pstmt.executeQuery()) {
+ return rs.next();
+ }
+ }
+ }
+ catch (SQLException | ClassNotFoundException ex) {
+ throw new RuntimeException("Error checking if user exists: " + ex.getMessage(), ex);
+ }
+ }
+
+ @Override
+ public void save(User user) {
+ final String sql = "INSERT INTO users (username, passwords, accessCode) VALUES (?, ?, ?) "
+ + "ON DUPLICATE KEY UPDATE passwords = VALUES(passwords), accessCode = VALUES(accessCode);";
+
+ try {
+ Class.forName(COM_MYSQL_CJ_JDBC_DRIVER);
+ try (Connection conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD);
+ PreparedStatement pstmt = conn.prepareStatement(sql)) {
+
+ pstmt.setString(ONE, user.getEmail());
+ pstmt.setString(TWO, user.getPassword());
+ pstmt.setString(THREE, user.getAccessCode());
+
+ pstmt.executeUpdate();
+ }
+ }
+ catch (SQLException | ClassNotFoundException ex) {
+ throw new RuntimeException("Error saving user: " + ex.getMessage(), ex);
+ }
+ }
+
+ @Override
+ public void changePassword(User user) {
+ final String sql = "INSERT INTO users (username, passwords, accessCode) VALUES (?, ?, ?) "
+ + "ON DUPLICATE KEY UPDATE passwords = VALUES(passwords), accessCode = VALUES(accessCode);";
+ try {
+ Class.forName(COM_MYSQL_CJ_JDBC_DRIVER);
+ try (Connection conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD);
+ PreparedStatement pstmt = conn.prepareStatement(sql)) {
+
+ pstmt.setString(ONE, user.getEmail());
+ pstmt.setString(TWO, user.getPassword());
+ pstmt.setString(THREE, user.getAccessCode());
+
+ pstmt.executeUpdate();
+ }
+ }
+ catch (SQLException | ClassNotFoundException ex) {
+ throw new RuntimeException("Error changing password: " + ex.getMessage(), ex);
+ }
+ }
+
+ @Override
+ public String getCurrentEmail() {
+ return currentEmail;
+ }
+
+ public void saveVerificationCode(String email, String verificationCode) {
+ final OkHttpClient client = new OkHttpClient.Builder().build();
+ final MediaType mediaType = MediaType.parse(CONTENT_TYPE_JSON);
+ final JSONObject requestBody = new JSONObject();
+ requestBody.put("email", email);
+ requestBody.put("verificationCode", verificationCode);
+ final RequestBody body = RequestBody.create(requestBody.toString(), mediaType);
+ final Request request = new Request.Builder()
+ .url("http://vm003.teach.cs.toronto.edu:20112/saveVerificationCode")
+ .post(body)
+ .addHeader(CONTENT_TYPE_LABEL, CONTENT_TYPE_JSON)
+ .build();
+ try (Response response = client.newCall(request).execute()) {
+ final JSONObject responseBody = new JSONObject(response.body().string());
+ if (responseBody.getInt(STATUS_CODE_LABEL) != SUCCESS_CODE) {
+ throw new RuntimeException(responseBody.getString(MESSAGE));
+ }
+ } catch (IOException | JSONException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public String getVerificationCode(String email) {
+ final OkHttpClient client = new OkHttpClient.Builder().build();
+ final Request request = new Request.Builder()
+ .url(String.format("http://vm003.teach.cs.toronto.edu:20112/getVerificationCode?email=%s", email))
+ .addHeader(CONTENT_TYPE_LABEL, CONTENT_TYPE_JSON)
+ .build();
+ try (Response response = client.newCall(request).execute()) {
+ final JSONObject responseBody = new JSONObject(response.body().string());
+ if (responseBody.getInt(STATUS_CODE_LABEL) == SUCCESS_CODE) {
+ return responseBody.getString("verificationCode");
+ } else {
+ throw new RuntimeException(responseBody.getString(MESSAGE));
+ }
+ } catch (IOException | JSONException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/src/main/java/osiris/data_access/EmailServiceImpl.java b/src/main/java/osiris/data_access/EmailServiceImpl.java
new file mode 100644
index 000000000..104868c09
--- /dev/null
+++ b/src/main/java/osiris/data_access/EmailServiceImpl.java
@@ -0,0 +1,55 @@
+package osiris.data_access;
+
+import java.util.Properties;
+
+import javax.mail.Authenticator;
+import javax.mail.Message;
+import javax.mail.MessagingException;
+import javax.mail.PasswordAuthentication;
+import javax.mail.Session;
+import javax.mail.Transport;
+import javax.mail.internet.InternetAddress;
+import javax.mail.internet.MimeMessage;
+
+public class EmailServiceImpl implements interface_adapter.EmailService {
+
+ private final String username;
+ private final String password;
+
+ public EmailServiceImpl() {
+ this.username = System.getenv("EMAIL_USERNAME");
+ this.password = System.getenv("EMAIL_PASSWORD");
+
+ if (username == null || password == null) {
+ throw new IllegalStateException("Environment variables EMAIL_USERNAME and EMAIL_PASSWORD must be set.");
+ }
+ }
+
+ @Override
+ public void sendVerificationEmail(String sendingAddress, String subject, String body) {
+ final Properties props = new Properties();
+ props.put("mail.smtp.host", "smtp-relay.brevo.com");
+ props.put("mail.smtp.port", "587");
+ props.put("mail.smtp.auth", "true");
+ props.put("mail.smtp.starttls.enable", "true");
+
+ final Session session = Session.getInstance(props, new Authenticator() {
+ protected PasswordAuthentication getPasswordAuthentication() {
+ return new PasswordAuthentication(username, password);
+ }
+ });
+
+ try {
+ final Message message = new MimeMessage(session);
+ message.setFrom(new InternetAddress(username));
+ message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(sendingAddress));
+ message.setSubject(subject);
+ message.setText(body);
+
+ Transport.send(message);
+ }
+ catch (MessagingException exception) {
+ throw new RuntimeException(exception);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/osiris/data_access/PlaidDataAccessObject.java b/src/main/java/osiris/data_access/PlaidDataAccessObject.java
new file mode 100644
index 000000000..2466c1c30
--- /dev/null
+++ b/src/main/java/osiris/data_access/PlaidDataAccessObject.java
@@ -0,0 +1,200 @@
+package osiris.data_access;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+import com.plaid.client.model.Transaction;
+import okhttp3.MediaType;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.RequestBody;
+import okhttp3.Response;
+import osiris.use_case.plaid.UserPlaidDataAccessInterface;
+import osiris.utility.exceptions.PlaidException;
+
+import java.io.IOException;
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Repository;
+
+
+@Repository
+public class PlaidDataAccessObject implements UserPlaidDataAccessInterface {
+
+ private static final MediaType JSON_MEDIA_TYPE = MediaType.parse("application/json; charset=utf-8");
+
+ private final OkHttpClient client;
+ private final Gson gson;
+
+ @Value("${plaid.client_id}")
+ private String clientId;
+
+ @Value("${plaid.secret}")
+ private String secret;
+
+ @Value("${plaid.environment}")
+ private String environment;
+
+ public PlaidDataAccessObject() {
+ this.client = new OkHttpClient();
+ this.gson = new Gson();
+ }
+
+ /**
+ * Determines the Plaid API base URL based on the environment.
+ *
+ * @param endpoint Specific Plaid API endpoint.
+ * @return Full URL to the Plaid API endpoint.
+ */
+ private String getPlaidUrl(String endpoint) {
+ switch (environment.toLowerCase()) {
+ case "development":
+ return "https://development.plaid.com/" + endpoint;
+ case "production":
+ return "https://production.plaid.com/" + endpoint;
+ case "sandbox":
+ default:
+ return "https://sandbox.plaid.com/" + endpoint;
+ }
+ }
+
+ /**
+ * Sends a POST request to the specified Plaid endpoint with the given JSON body.
+ *
+ * @param endpoint The specific Plaid API endpoint.
+ * @param jsonBody The JSON body to send in the request.
+ * @return The JSON response from Plaid as a string.
+ * @throws IOException If an I/O error occurs during the API call.
+ * @throws PlaidException If the Plaid API returns an unsuccessful response.
+ */
+ private String postToPlaid(String endpoint, JsonObject jsonBody) throws IOException {
+ String url = getPlaidUrl(endpoint);
+
+ RequestBody body = RequestBody.create(JSON_MEDIA_TYPE, gson.toJson(jsonBody));
+
+ Request request = new Request.Builder()
+ .url(url)
+ .post(body)
+ .addHeader("Content-Type", "application/json")
+ .build();
+
+ try (Response response = client.newCall(request).execute()) {
+ if (!response.isSuccessful()) {
+ throw new PlaidException("Plaid API Error: " + response.code() + " - " + response.message());
+ }
+
+ return response.body().string();
+ }
+ }
+
+ /**
+ * Creates a Link Token by communicating with Plaid's API.
+ *
+ * @param clientName Name of your application.
+ * @param countryCodes Array of country codes (e.g., ["US"]).
+ * @param language Language code (e.g., "en").
+ * @param userClientId Unique identifier for the user in your system.
+ * @param products Array of products (e.g., ["auth", "transactions"]).
+ * @return LinkTokenResponse containing the link token.
+ * @throws IOException If an I/O error occurs during the API call.
+ */
+ public LinkTokenResponse createLinkToken(String clientName, String[] countryCodes, String language,
+ String userClientId, String[] products) throws IOException {
+ JsonObject requestBody = new JsonObject();
+ requestBody.addProperty("client_id", clientId);
+ requestBody.addProperty("secret", secret);
+ requestBody.addProperty("client_name", clientName);
+ requestBody.addProperty("language", language);
+ requestBody.add("country_codes", gson.toJsonTree(countryCodes));
+
+ JsonObject user = new JsonObject();
+ user.addProperty("client_user_id", userClientId);
+ requestBody.add("user", user);
+
+ requestBody.add("products", gson.toJsonTree(products));
+
+ String responseBody = postToPlaid("link/token/create", requestBody);
+ return gson.fromJson(responseBody, LinkTokenResponse.class);
+ }
+
+ /**
+ * Exchanges a Public Token for an Access Token.
+ *
+ * @param publicToken The public token received from Plaid Link.
+ * @return ExchangeTokenResponse containing the access token and item ID.
+ * @throws IOException If an I/O error occurs during the API call.
+ */
+ public ExchangeTokenResponse exchangePublicToken(String publicToken) throws IOException {
+ JsonObject requestBody = new JsonObject();
+ requestBody.addProperty("client_id", clientId);
+ requestBody.addProperty("secret", secret);
+ requestBody.addProperty("public_token", publicToken);
+
+ String responseBody = postToPlaid("item/public_token/exchange", requestBody);
+ return gson.fromJson(responseBody, ExchangeTokenResponse.class);
+ }
+
+
+ /**
+ * Represents the response from Plaid when creating a Link Token.
+ */
+ public static class LinkTokenResponse {
+ public String link_token;
+ public String request_id;
+
+ public LinkTokenResponse(String testLinkToken, String testRequestId) {
+ this.link_token = testLinkToken;
+ this.request_id = testRequestId;
+ }
+ }
+
+ /**
+ * Represents the response from Plaid when exchanging a Public Token.
+ */
+ public static class ExchangeTokenResponse {
+ public String access_token;
+ public String item_id;
+ public String request_id;
+
+ public ExchangeTokenResponse(String testAccessToken, String testItemId, String testRequestId) {
+ this.access_token = testAccessToken;
+ this.item_id = testItemId;
+ this.request_id = testRequestId;
+ }
+ }
+
+ /**
+ * Represents the response from Plaid when exchanging a access Token for transactions.
+ * @param accessToken Name of your token.
+ * @return fetchTransactions containing the transactions.
+ * @throws IOException If an I/O error occurs during the API call.
+ */
+
+ public String fetchTransactions(String accessToken) throws IOException {
+ final LocalDate startDate = LocalDate.now().minusDays(30);
+ final LocalDate endDate = LocalDate.now();
+
+ final JsonObject requestBody = new JsonObject();
+ requestBody.addProperty("client_id", clientId);
+ requestBody.addProperty("secret", secret);
+ requestBody.addProperty("access_token", accessToken);
+ requestBody.addProperty("start_date", String.valueOf(startDate));
+ requestBody.addProperty("end_date", String.valueOf(endDate));
+
+ final RequestBody body = RequestBody.create(JSON_MEDIA_TYPE, gson.toJson(requestBody));
+ final Request request = new Request.Builder()
+ .url("https://sandbox.plaid.com/transactions/get")
+ .post(body)
+ .addHeader("Content-Type", "application/json")
+ .build();
+
+ try (Response response = client.newCall(request).execute()) {
+ if (!response.isSuccessful()) {
+ throw new IOException("Plaid API Error: " + response.code() + " - " + response.message());
+ }
+ return response.body().string();
+ }
+ }
+}
diff --git a/src/main/java/entity/CommonUser.java b/src/main/java/osiris/entity/CommonUser.java
similarity index 55%
rename from src/main/java/entity/CommonUser.java
rename to src/main/java/osiris/entity/CommonUser.java
index ba25fd20a..8a1beb22c 100644
--- a/src/main/java/entity/CommonUser.java
+++ b/src/main/java/osiris/entity/CommonUser.java
@@ -1,4 +1,4 @@
-package entity;
+package osiris.entity;
/**
* A simple implementation of the User interface.
@@ -7,14 +7,16 @@ public class CommonUser implements User {
private final String name;
private final String password;
+ private final String accessCode;
- public CommonUser(String name, String password) {
+ public CommonUser(String name, String password, String access_code) {
this.name = name;
this.password = password;
+ this.accessCode = access_code;
}
@Override
- public String getName() {
+ public String getEmail() {
return name;
}
@@ -23,4 +25,9 @@ public String getPassword() {
return password;
}
+ @Override
+ public String getAccessCode() {
+ return accessCode;
+ }
+
}
diff --git a/src/main/java/osiris/entity/CommonUserFactory.java b/src/main/java/osiris/entity/CommonUserFactory.java
new file mode 100644
index 000000000..8316a5632
--- /dev/null
+++ b/src/main/java/osiris/entity/CommonUserFactory.java
@@ -0,0 +1,15 @@
+package osiris.entity;
+
+import org.springframework.stereotype.Component;
+
+/**
+ * Factory for creating CommonUser objects.
+ */
+@Component
+public class CommonUserFactory implements UserFactory {
+
+ @Override
+ public User create(String name, String password, String access_code) {
+ return new CommonUser(name, password, access_code);
+ }
+}
diff --git a/src/main/java/entity/User.java b/src/main/java/osiris/entity/User.java
similarity index 63%
rename from src/main/java/entity/User.java
rename to src/main/java/osiris/entity/User.java
index 0ad073902..f3c931a1d 100644
--- a/src/main/java/entity/User.java
+++ b/src/main/java/osiris/entity/User.java
@@ -1,4 +1,4 @@
-package entity;
+package osiris.entity;
/**
* The representation of a user in our program.
@@ -9,7 +9,7 @@ public interface User {
* Returns the username of the user.
* @return the username of the user.
*/
- String getName();
+ String getEmail();
/**
* Returns the password of the user.
@@ -17,4 +17,10 @@ public interface User {
*/
String getPassword();
+ /**
+ * Returns the access code of the user.
+ * @return the access code of the user.
+ */
+ String getAccessCode();
+
}
diff --git a/src/main/java/osiris/entity/UserFactory.java b/src/main/java/osiris/entity/UserFactory.java
new file mode 100644
index 000000000..6541742be
--- /dev/null
+++ b/src/main/java/osiris/entity/UserFactory.java
@@ -0,0 +1,17 @@
+package osiris.entity;
+
+/**
+ * Factory for creating users.
+ */
+public interface UserFactory {
+ /**
+ * Creates a new User.
+ *
+ * @param name the name of the new user
+ * @param password the password of the new user
+ * @param accessCode the user's Plaid
+ * @return the new user
+ */
+ User create(String name, String password, String accessCode);
+
+}
diff --git a/src/main/java/interface_adapter/ViewManagerModel.java b/src/main/java/osiris/interface_adapter/ViewManagerModel.java
similarity index 89%
rename from src/main/java/interface_adapter/ViewManagerModel.java
rename to src/main/java/osiris/interface_adapter/ViewManagerModel.java
index 99dc0ffe5..c4f7a660e 100644
--- a/src/main/java/interface_adapter/ViewManagerModel.java
+++ b/src/main/java/osiris/interface_adapter/ViewManagerModel.java
@@ -1,4 +1,4 @@
-package interface_adapter;
+package osiris.interface_adapter;
/**
* Model for the View Manager. Its state is the name of the View which
diff --git a/src/main/java/interface_adapter/ViewModel.java b/src/main/java/osiris/interface_adapter/ViewModel.java
similarity index 98%
rename from src/main/java/interface_adapter/ViewModel.java
rename to src/main/java/osiris/interface_adapter/ViewModel.java
index bc2dea65b..6d3f07ad6 100644
--- a/src/main/java/interface_adapter/ViewModel.java
+++ b/src/main/java/osiris/interface_adapter/ViewModel.java
@@ -1,4 +1,4 @@
-package interface_adapter;
+package osiris.interface_adapter;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
diff --git a/src/main/java/osiris/interface_adapter/budget/BudgetController.java b/src/main/java/osiris/interface_adapter/budget/BudgetController.java
new file mode 100644
index 000000000..bf91bf6f5
--- /dev/null
+++ b/src/main/java/osiris/interface_adapter/budget/BudgetController.java
@@ -0,0 +1,32 @@
+package osiris.interface_adapter.budget;
+
+import osiris.use_case.budget.BudgetInputBoundary;
+import osiris.use_case.budget.BudgetInputData;
+
+/**
+ * The controller for the BudgetExpenses Use Case.
+ */
+public class BudgetController {
+ private final BudgetInputBoundary interactor;
+
+ public BudgetController(BudgetInputBoundary interactor) {
+ this.interactor = interactor;
+ }
+
+ /**
+ * Executes the budget Use Case.
+ * @param transactionList List of transactions for a given access token
+ */
+
+ public void execute(String transactionList) {
+ final BudgetInputData inputData = new BudgetInputData(transactionList);
+ interactor.execute(inputData);
+ }
+
+ /**
+ * Executes the "switch to Dashboard" Use Case.
+ */
+ public void switchToHomeView() {
+ interactor.switchToDashboardView();
+ }
+}
diff --git a/src/main/java/osiris/interface_adapter/budget/BudgetPresenter.java b/src/main/java/osiris/interface_adapter/budget/BudgetPresenter.java
new file mode 100644
index 000000000..5d980bc4b
--- /dev/null
+++ b/src/main/java/osiris/interface_adapter/budget/BudgetPresenter.java
@@ -0,0 +1,27 @@
+package osiris.interface_adapter.budget;
+
+import osiris.interface_adapter.ViewManagerModel;
+import osiris.interface_adapter.dashboard.DashboardViewModel;
+import osiris.use_case.budget.BudgetOutputBoundary;
+
+/**
+ * The Presenter for the budget Use Case.
+ */
+public class BudgetPresenter implements BudgetOutputBoundary {
+ private final BudgetViewModel budgetViewModel;
+ private final ViewManagerModel viewManagerModel;
+ private DashboardViewModel dashboardViewModel;
+
+ public BudgetPresenter(BudgetViewModel budgetViewModel,
+ ViewManagerModel viewManagerModel) {
+ this.budgetViewModel = budgetViewModel;
+ this.viewManagerModel = viewManagerModel;
+ }
+
+ @Override
+ public void switchToDashboardView() {
+ viewManagerModel.setState("dashboard");
+ viewManagerModel.firePropertyChanged();
+ }
+
+}
diff --git a/src/main/java/osiris/interface_adapter/budget/BudgetState.java b/src/main/java/osiris/interface_adapter/budget/BudgetState.java
new file mode 100644
index 000000000..f816a3e66
--- /dev/null
+++ b/src/main/java/osiris/interface_adapter/budget/BudgetState.java
@@ -0,0 +1,19 @@
+package osiris.interface_adapter.budget;
+
+import java.util.ArrayList;
+
+/**
+ * The state for the budget Model.
+ */
+
+public class BudgetState {
+ private ArrayList monthlyTotals;
+
+ public ArrayList getMonthlyTotals() {
+ return monthlyTotals;
+ }
+
+ public void setMonthlyTotals(ArrayList monthlyTotals) {
+ this.monthlyTotals = monthlyTotals;
+ }
+}
diff --git a/src/main/java/osiris/interface_adapter/budget/BudgetViewModel.java b/src/main/java/osiris/interface_adapter/budget/BudgetViewModel.java
new file mode 100644
index 000000000..71e3f7241
--- /dev/null
+++ b/src/main/java/osiris/interface_adapter/budget/BudgetViewModel.java
@@ -0,0 +1,20 @@
+package osiris.interface_adapter.budget;
+
+import osiris.interface_adapter.ViewModel;
+
+/**
+ * The ViewModel for the budget.
+ */
+public class BudgetViewModel extends ViewModel {
+ public static final String TITLE_LABEL = "Monthly Spending";
+ public static final String BUTTON_LABEL = "Budget";
+ public static final String BACK_LABEL = "Back";
+
+ public BudgetViewModel() {
+
+ super("budget");
+ setState(new BudgetState());
+ }
+
+}
+
diff --git a/src/main/java/interface_adapter/change_password/ChangePasswordController.java b/src/main/java/osiris/interface_adapter/change_password/ChangePasswordController.java
similarity index 68%
rename from src/main/java/interface_adapter/change_password/ChangePasswordController.java
rename to src/main/java/osiris/interface_adapter/change_password/ChangePasswordController.java
index a7abd075f..f8f34a4b1 100644
--- a/src/main/java/interface_adapter/change_password/ChangePasswordController.java
+++ b/src/main/java/osiris/interface_adapter/change_password/ChangePasswordController.java
@@ -1,7 +1,7 @@
-package interface_adapter.change_password;
+package osiris.interface_adapter.change_password;
-import use_case.change_password.ChangePasswordInputBoundary;
-import use_case.change_password.ChangePasswordInputData;
+import osiris.use_case.change_password.ChangePasswordInputBoundary;
+import osiris.use_case.change_password.ChangePasswordInputData;
/**
* Controller for the Change Password Use Case.
@@ -18,8 +18,8 @@ public ChangePasswordController(ChangePasswordInputBoundary userChangePasswordUs
* @param password the new password
* @param username the user whose password to change
*/
- public void execute(String password, String username) {
- final ChangePasswordInputData changePasswordInputData = new ChangePasswordInputData(username, password);
+ public void execute(String password, String username, String access_code) {
+ final ChangePasswordInputData changePasswordInputData = new ChangePasswordInputData(username, password, access_code);
userChangePasswordUseCaseInteractor.execute(changePasswordInputData);
}
diff --git a/src/main/java/interface_adapter/change_password/ChangePasswordPresenter.java b/src/main/java/osiris/interface_adapter/change_password/ChangePasswordPresenter.java
similarity index 83%
rename from src/main/java/interface_adapter/change_password/ChangePasswordPresenter.java
rename to src/main/java/osiris/interface_adapter/change_password/ChangePasswordPresenter.java
index 3efca4e46..af5cd5c1a 100644
--- a/src/main/java/interface_adapter/change_password/ChangePasswordPresenter.java
+++ b/src/main/java/osiris/interface_adapter/change_password/ChangePasswordPresenter.java
@@ -1,7 +1,7 @@
-package interface_adapter.change_password;
+package osiris.interface_adapter.change_password;
-import use_case.change_password.ChangePasswordOutputBoundary;
-import use_case.change_password.ChangePasswordOutputData;
+import osiris.use_case.change_password.ChangePasswordOutputBoundary;
+import osiris.use_case.change_password.ChangePasswordOutputData;
/**
* The Presenter for the Change Password Use Case.
diff --git a/src/main/java/interface_adapter/change_password/LoggedInState.java b/src/main/java/osiris/interface_adapter/change_password/LoggedInState.java
similarity index 67%
rename from src/main/java/interface_adapter/change_password/LoggedInState.java
rename to src/main/java/osiris/interface_adapter/change_password/LoggedInState.java
index 54ed80537..20a8da012 100644
--- a/src/main/java/interface_adapter/change_password/LoggedInState.java
+++ b/src/main/java/osiris/interface_adapter/change_password/LoggedInState.java
@@ -1,16 +1,17 @@
-package interface_adapter.change_password;
+package osiris.interface_adapter.change_password;
/**
* The State information representing the logged-in user.
*/
public class LoggedInState {
- private String username = "";
+ private String email = "";
private String password = "";
+ private String accessCode = "";
private String passwordError;
public LoggedInState(LoggedInState copy) {
- username = copy.username;
+ email = copy.email;
password = copy.password;
passwordError = copy.passwordError;
}
@@ -20,12 +21,12 @@ public LoggedInState() {
}
- public String getUsername() {
- return username;
+ public String getEmail() {
+ return email;
}
- public void setUsername(String username) {
- this.username = username;
+ public void setEmail(String email) {
+ this.email = email;
}
public void setPassword(String password) {
@@ -39,4 +40,8 @@ public void setPasswordError(String passwordError) {
public String getPassword() {
return password;
}
+
+ public String getAccessCode() {
+ return accessCode;
+ }
}
diff --git a/src/main/java/interface_adapter/change_password/LoggedInViewModel.java b/src/main/java/osiris/interface_adapter/change_password/LoggedInViewModel.java
similarity index 71%
rename from src/main/java/interface_adapter/change_password/LoggedInViewModel.java
rename to src/main/java/osiris/interface_adapter/change_password/LoggedInViewModel.java
index 85c81e4e6..9e11f4753 100644
--- a/src/main/java/interface_adapter/change_password/LoggedInViewModel.java
+++ b/src/main/java/osiris/interface_adapter/change_password/LoggedInViewModel.java
@@ -1,6 +1,6 @@
-package interface_adapter.change_password;
+package osiris.interface_adapter.change_password;
-import interface_adapter.ViewModel;
+import osiris.interface_adapter.ViewModel;
/**
* The View Model for the Logged In View.
diff --git a/src/main/java/osiris/interface_adapter/dashboard/DashboardController.java b/src/main/java/osiris/interface_adapter/dashboard/DashboardController.java
new file mode 100644
index 000000000..0c9870766
--- /dev/null
+++ b/src/main/java/osiris/interface_adapter/dashboard/DashboardController.java
@@ -0,0 +1,34 @@
+package osiris.interface_adapter.dashboard;
+
+import osiris.use_case.dashboard.DashboardInputBoundary;
+
+/**
+ * Controller for the Dashboard Use Case.
+ */
+public class DashboardController {
+
+ private final DashboardInputBoundary userDashboardUseCaseInteractor;
+
+ public DashboardController(DashboardInputBoundary userDashboardUseCaseInteractor) {
+ this.userDashboardUseCaseInteractor = userDashboardUseCaseInteractor;
+ }
+
+ /**
+ * Executes the "switch to addBankAccountView" Use Case.
+ */
+ public void switchToLoginView() {
+ userDashboardUseCaseInteractor.switchToAddBankAccountView();
+ }
+
+ /**
+ * Executes the "switch to ViewExpenses" Use Case.
+ */
+ public void switchToViewExpenses() {
+ userDashboardUseCaseInteractor.switchToViewExpenses();
+ }
+
+ /**
+ * Executes the "switch to BudgetView" Use Case.
+ */
+ public void switchToBudgetView() { userDashboardUseCaseInteractor.switchToBudgetView(); }
+}
diff --git a/src/main/java/osiris/interface_adapter/dashboard/DashboardPresenter.java b/src/main/java/osiris/interface_adapter/dashboard/DashboardPresenter.java
new file mode 100644
index 000000000..bf507370a
--- /dev/null
+++ b/src/main/java/osiris/interface_adapter/dashboard/DashboardPresenter.java
@@ -0,0 +1,44 @@
+package osiris.interface_adapter.dashboard;
+
+import osiris.interface_adapter.ViewManagerModel;
+import osiris.interface_adapter.budget.BudgetViewModel;
+import osiris.interface_adapter.viewexpenses.ViewExpensesViewModel;
+import osiris.use_case.dashboard.DashboardOutputBoundary;
+
+/**
+ * Presenter for the Welcome Use Case.
+ */
+public class DashboardPresenter implements DashboardOutputBoundary {
+
+ private final ViewManagerModel viewManagerModel;
+ private final DashboardViewModel dashboardViewModel;
+ private final ViewExpensesViewModel viewExpensesViewModel;
+ private final BudgetViewModel budgetViewModel;
+
+ public DashboardPresenter(ViewManagerModel viewManagerModel,
+ DashboardViewModel dashboardViewModel, ViewExpensesViewModel viewExpensesViewModel,
+ BudgetViewModel budgetViewModel) {
+ this.viewManagerModel = viewManagerModel;
+ this.dashboardViewModel = dashboardViewModel;
+ this.viewExpensesViewModel = viewExpensesViewModel;
+ this.budgetViewModel = budgetViewModel;
+ }
+
+ @Override
+ public void switchToAddBankAccountView() {
+
+ }
+
+ @Override
+ public void switchToViewExpenses() {
+ viewManagerModel.setState("view expenses");
+ viewManagerModel.firePropertyChanged();
+ }
+
+ @Override
+ public void switchToBudgetView() {
+ viewManagerModel.setState("budget");
+ viewManagerModel.firePropertyChanged();
+ }
+
+}
diff --git a/src/main/java/osiris/interface_adapter/dashboard/DashboardViewModel.java b/src/main/java/osiris/interface_adapter/dashboard/DashboardViewModel.java
new file mode 100644
index 000000000..ba32bcb99
--- /dev/null
+++ b/src/main/java/osiris/interface_adapter/dashboard/DashboardViewModel.java
@@ -0,0 +1,19 @@
+package osiris.interface_adapter.dashboard;
+
+/**
+ * The ViewModel for the Welcome View.
+ */
+public class DashboardViewModel {
+
+ public static final String ADD_BANK_ACCOUNT_BUTTON_LABEL = "Add Bank";
+ public static final String VIEW_BUDGET_BUTTON_LABEL = "View Budget";
+ public static final String VIEW_SHAHCASE_BUTTON_LABEL = "Shah Case";
+ public static final String VIEW_TRANSACTION_HISTORY_BUTTON_LABEL = "View Expenses";
+
+ public DashboardViewModel() {
+ }
+
+ public String getViewName() {
+ return "dashboard";
+ }
+}
diff --git a/src/main/java/osiris/interface_adapter/grabtransaction/GrabTransactionController.java b/src/main/java/osiris/interface_adapter/grabtransaction/GrabTransactionController.java
new file mode 100644
index 000000000..fba468fa3
--- /dev/null
+++ b/src/main/java/osiris/interface_adapter/grabtransaction/GrabTransactionController.java
@@ -0,0 +1,33 @@
+package osiris.interface_adapter.grabtransaction;
+
+import java.io.IOException;
+
+import osiris.use_case.grabtransactions.GrabTransactionOutputData;
+import osiris.use_case.grabtransactions.GrabTransactionsInputBoundary;
+import osiris.use_case.grabtransactions.GrabTransactionsInputData;
+import osiris.utility.exceptions.PlaidUseCaseException;
+
+/**
+ * The controller for the GrabTransaction Use Case.
+ */
+public class GrabTransactionController {
+ private final GrabTransactionsInputBoundary grabTransactions;
+
+ public GrabTransactionController(GrabTransactionsInputBoundary grabTransactions) {
+ this.grabTransactions = grabTransactions;
+ }
+
+ /**
+ * Endpoint to create a Plaid Link Token.
+ *
+ * @param username The request body containing necessary parameters.
+ * @return GrabTransactionOutputData containing the List of Transactions
+ * @throws PlaidUseCaseException when Plaid fails
+ * @throws IOException when performing input/output operation
+ */
+ public GrabTransactionOutputData createTransactions(String username) throws PlaidUseCaseException, IOException {
+ final GrabTransactionsInputData inputData = new GrabTransactionsInputData(username);
+ return grabTransactions.fetchTransactions(inputData);
+ }
+
+}
diff --git a/src/main/java/interface_adapter/login/LoginController.java b/src/main/java/osiris/interface_adapter/login/LoginController.java
similarity index 54%
rename from src/main/java/interface_adapter/login/LoginController.java
rename to src/main/java/osiris/interface_adapter/login/LoginController.java
index 57e950666..081a73ad7 100644
--- a/src/main/java/interface_adapter/login/LoginController.java
+++ b/src/main/java/osiris/interface_adapter/login/LoginController.java
@@ -1,7 +1,7 @@
-package interface_adapter.login;
+package osiris.interface_adapter.login;
-import use_case.login.LoginInputBoundary;
-import use_case.login.LoginInputData;
+import osiris.use_case.login.LoginInputBoundary;
+import osiris.use_case.login.LoginInputData;
/**
* The controller for the Login Use Case.
@@ -18,11 +18,19 @@ public LoginController(LoginInputBoundary loginUseCaseInteractor) {
* Executes the Login Use Case.
* @param username the username of the user logging in
* @param password the password of the user logging in
+ * @param access_code the access code of the user logging in
*/
- public void execute(String username, String password) {
+ public void execute(String username, String password, String access_code) {
final LoginInputData loginInputData = new LoginInputData(
- username, password);
+ username, password, access_code);
loginUseCaseInteractor.execute(loginInputData);
}
+
+ /**
+ * Executes the "switch to WelcomeView" Use Case.
+ */
+ public void switchToWelcomeView() {
+ loginUseCaseInteractor.switchToWelcomeView();
+ }
}
diff --git a/src/main/java/osiris/interface_adapter/login/LoginPresenter.java b/src/main/java/osiris/interface_adapter/login/LoginPresenter.java
new file mode 100644
index 000000000..b35867912
--- /dev/null
+++ b/src/main/java/osiris/interface_adapter/login/LoginPresenter.java
@@ -0,0 +1,74 @@
+package osiris.interface_adapter.login;
+
+import osiris.interface_adapter.ViewManagerModel;
+import osiris.interface_adapter.change_password.LoggedInState;
+import osiris.interface_adapter.change_password.LoggedInViewModel;
+import osiris.interface_adapter.dashboard.DashboardViewModel;
+import osiris.interface_adapter.verify.VerifyState;
+import osiris.interface_adapter.verify.VerifyViewModel;
+import osiris.interface_adapter.welcome.WelcomeViewModel;
+import osiris.use_case.login.LoginOutputBoundary;
+import osiris.use_case.login.LoginOutputData;
+
+/**
+ * The Presenter for the Login Use Case.
+ */
+public class LoginPresenter implements LoginOutputBoundary {
+
+ private final LoginViewModel loginViewModel;
+ private final VerifyViewModel verifyViewModel;
+ // private final LoggedInViewModel loggedInViewModel;
+ private final ViewManagerModel viewManagerModel;
+ private final WelcomeViewModel welcomeViewModel;
+ private final DashboardViewModel dashboardViewModel;
+
+ public LoginPresenter(ViewManagerModel viewManagerModel,
+ LoginViewModel loginViewModel, WelcomeViewModel welcomeViewModel,
+ VerifyViewModel verifyViewModel, DashboardViewModel dashboardViewModel) {
+ this.viewManagerModel = viewManagerModel;
+ this.verifyViewModel = verifyViewModel;
+ this.loginViewModel = loginViewModel;
+ this.welcomeViewModel = welcomeViewModel;
+ this.dashboardViewModel = dashboardViewModel;
+ }
+
+ @Override
+ public void prepareSuccessView(LoginOutputData response) {
+ // On success, switch to the logged in view.
+
+ /**
+ final VerifyState verifyState = verifyViewModel.getState();
+ verifyState.setUsername(response.getEmail());
+ this.verifyViewModel.setState(verifyState);
+ this.verifyViewModel.firePropertyChanged();
+ this.viewManagerModel.setState(verifyViewModel.getViewName());
+ this.viewManagerModel.firePropertyChanged();
+ **/
+
+ viewManagerModel.setState(dashboardViewModel.getViewName());
+ viewManagerModel.firePropertyChanged();
+ /**
+ final LoggedInState loggedInState = loggedInViewModel.getState();
+ loggedInState.setUsername(response.getUsername());
+ this.loggedInViewModel.setState(loggedInState);
+ this.loggedInViewModel.firePropertyChanged();
+
+ this.viewManagerModel.setState(loggedInViewModel.getViewName());
+ this.viewManagerModel.firePropertyChanged();
+ **/
+
+ }
+
+ @Override
+ public void prepareFailView(String error) {
+ final LoginState loginState = loginViewModel.getState();
+ loginState.setLoginError(error);
+ loginViewModel.firePropertyChanged();
+ }
+
+ @Override
+ public void switchToWelcomeView() {
+ viewManagerModel.setState(welcomeViewModel.getViewName());
+ viewManagerModel.firePropertyChanged();
+ }
+}
diff --git a/src/main/java/interface_adapter/login/LoginState.java b/src/main/java/osiris/interface_adapter/login/LoginState.java
similarity index 51%
rename from src/main/java/interface_adapter/login/LoginState.java
rename to src/main/java/osiris/interface_adapter/login/LoginState.java
index e42632034..4284c29b3 100644
--- a/src/main/java/interface_adapter/login/LoginState.java
+++ b/src/main/java/osiris/interface_adapter/login/LoginState.java
@@ -1,15 +1,16 @@
-package interface_adapter.login;
+package osiris.interface_adapter.login;
/**
* The state for the Login View Model.
*/
public class LoginState {
- private String username = "";
+ private String email = "";
private String loginError;
private String password = "";
+ private String access_code = "";
- public String getUsername() {
- return username;
+ public String getEmail() {
+ return email;
}
public String getLoginError() {
@@ -20,8 +21,8 @@ public String getPassword() {
return password;
}
- public void setUsername(String username) {
- this.username = username;
+ public void setEmail(String email) {
+ this.email = email;
}
public void setLoginError(String usernameError) {
@@ -32,4 +33,14 @@ public void setPassword(String password) {
this.password = password;
}
+ public void setAccessCode(String s) {
+ }
+
+ public String getAccessCode() {
+ return access_code;
+ }
+
+ public void setAccess_code(String access_code) {
+ this.access_code = access_code;
+ }
}
diff --git a/src/main/java/interface_adapter/login/LoginViewModel.java b/src/main/java/osiris/interface_adapter/login/LoginViewModel.java
similarity index 71%
rename from src/main/java/interface_adapter/login/LoginViewModel.java
rename to src/main/java/osiris/interface_adapter/login/LoginViewModel.java
index 41bea7a2e..becf52974 100644
--- a/src/main/java/interface_adapter/login/LoginViewModel.java
+++ b/src/main/java/osiris/interface_adapter/login/LoginViewModel.java
@@ -1,6 +1,6 @@
-package interface_adapter.login;
+package osiris.interface_adapter.login;
-import interface_adapter.ViewModel;
+import osiris.interface_adapter.ViewModel;
/**
* The View Model for the Login View.
diff --git a/src/main/java/osiris/interface_adapter/logout/LogoutController.java b/src/main/java/osiris/interface_adapter/logout/LogoutController.java
new file mode 100644
index 000000000..9689b6347
--- /dev/null
+++ b/src/main/java/osiris/interface_adapter/logout/LogoutController.java
@@ -0,0 +1,26 @@
+package osiris.interface_adapter.logout;
+
+import osiris.use_case.logout.LogoutInputBoundary;
+import osiris.use_case.logout.LogoutInputData;
+
+/**
+ * The controller for the Logout Use Case.
+ */
+public class LogoutController {
+
+ private final LogoutInputBoundary logoutUseCaseInteractor;
+
+ public LogoutController(LogoutInputBoundary logoutUseCaseInteractor) {
+ this.logoutUseCaseInteractor = logoutUseCaseInteractor;
+ }
+
+ /**
+ * Executes the Logout Use Case.
+ * @param username the username of the user logging in
+ */
+ public void execute(String username) {
+ final LogoutInputData inputData = new LogoutInputData(username);
+ logoutUseCaseInteractor.execute(inputData);
+
+ }
+}
diff --git a/src/main/java/osiris/interface_adapter/logout/LogoutPresenter.java b/src/main/java/osiris/interface_adapter/logout/LogoutPresenter.java
new file mode 100644
index 000000000..4f69d9537
--- /dev/null
+++ b/src/main/java/osiris/interface_adapter/logout/LogoutPresenter.java
@@ -0,0 +1,52 @@
+package osiris.interface_adapter.logout;
+
+import osiris.interface_adapter.ViewManagerModel;
+import osiris.interface_adapter.change_password.LoggedInState;
+import osiris.interface_adapter.change_password.LoggedInViewModel;
+import osiris.interface_adapter.login.LoginState;
+import osiris.interface_adapter.login.LoginViewModel;
+import osiris.use_case.logout.LogoutOutputBoundary;
+import osiris.use_case.logout.LogoutOutputData;
+
+/**
+ * The Presenter for the Logout Use Case.
+ */
+public class LogoutPresenter implements LogoutOutputBoundary {
+
+ private final LoggedInViewModel loggedInViewModel;
+ private final ViewManagerModel viewManagerModel;
+ private final LoginViewModel loginViewModel;
+
+ public LogoutPresenter(ViewManagerModel viewManagerModel,
+ LoggedInViewModel loggedInViewModel,
+ LoginViewModel loginViewModel) {
+ this.viewManagerModel = viewManagerModel;
+ this.loggedInViewModel = loggedInViewModel;
+ this.loginViewModel = loginViewModel;
+ }
+
+ @Override
+ public void prepareSuccessView(LogoutOutputData response) {
+ final LoggedInState state = loggedInViewModel.getState();
+ state.setEmail("");
+ loggedInViewModel.setState(state);
+ loggedInViewModel.firePropertyChanged();
+
+ final LoginState loginState = loginViewModel.getState();
+ loginState.setEmail("");
+ loginState.setPassword("");
+ loginState.setAccessCode("");
+ loginViewModel.setState(loginState);
+ loginViewModel.firePropertyChanged();
+
+ // This code tells the View Manager to switch to the LoginView.
+ this.viewManagerModel.setState(loginViewModel.getViewName());
+ this.viewManagerModel.firePropertyChanged();
+ }
+
+ @Override
+ public void prepareFailView(String error) {
+ // No need to add code here. We'll assume that logout can't fail.
+ // Thought question: is this a reasonable assumption?
+ }
+}
diff --git a/src/main/java/osiris/interface_adapter/plaid/PlaidController.java b/src/main/java/osiris/interface_adapter/plaid/PlaidController.java
new file mode 100644
index 000000000..a021d02f6
--- /dev/null
+++ b/src/main/java/osiris/interface_adapter/plaid/PlaidController.java
@@ -0,0 +1,83 @@
+package osiris.interface_adapter.plaid;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
+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 osiris.use_case.plaid.CreateLinkTokenInputData;
+import osiris.use_case.plaid.CreateLinkTokenOutputData;
+import osiris.use_case.plaid.ExchangePublicTokenInputData;
+import osiris.use_case.plaid.ExchangePublicTokenOutputData;
+import osiris.use_case.plaid.PlaidInputBoundary;
+import osiris.utility.data_transfer_objects.ExchangeTokenRequestDataTransferObject;
+import osiris.utility.data_transfer_objects.LinkTokenRequestDataTransferObject;
+import osiris.utility.exceptions.PlaidUseCaseException;
+
+import java.sql.SQLException;
+
+/**
+ * REST Controller for Plaid-related operations.
+ */
+@RestController
+@RequestMapping("/api/plaid")
+public class PlaidController {
+
+ private final PlaidInputBoundary plaidInteractor;
+ private String email;
+ private String password;
+
+ @Autowired
+ public PlaidController(PlaidInputBoundary plaidInteractor) {
+ this.plaidInteractor = plaidInteractor;
+ }
+
+ /**
+ * Endpoint to create a Plaid Link Token.
+ *
+ * @param linkTokenRequest The request body containing necessary parameters.
+ * @return CreateLinkTokenOutputData containing the link token.
+ * @throws Exception If an error occurs while creating the link token.
+ */
+ @PostMapping("/create-link-token")
+ public ResponseEntity createLinkToken(
+ @RequestBody LinkTokenRequestDataTransferObject linkTokenRequest)
+ throws Exception {
+
+ final CreateLinkTokenInputData inputData = new CreateLinkTokenInputData(
+ linkTokenRequest.getClientName(),
+ linkTokenRequest.getCountryCodes(),
+ linkTokenRequest.getLanguage(),
+ linkTokenRequest.getUserClientId(),
+ linkTokenRequest.getProducts()
+ );
+
+ final CreateLinkTokenOutputData outputData = plaidInteractor.createLinkToken(inputData);
+ return ResponseEntity.ok(outputData);
+ }
+
+ /**
+ * Endpoint to exchange a Public Token for an Access Token.
+ *
+ * @param exchangeTokenRequest The request body containing the public token.
+ * @return ExchangePublicTokenOutputData containing the access token and item ID.
+ * @throws PlaidUseCaseException If an error occurs while exchanging the public token.
+ * @throws SQLException If an error occurs while exchanging the public token.
+ */
+ @PostMapping("/exchange-public-token")
+ public ResponseEntity exchangePublicToken(
+ @RequestBody ExchangeTokenRequestDataTransferObject exchangeTokenRequest)
+ throws PlaidUseCaseException, SQLException {
+
+ final ExchangePublicTokenInputData inputData = new ExchangePublicTokenInputData(
+ exchangeTokenRequest.getPublicToken(),
+ exchangeTokenRequest.getUserClientId()
+ );
+ final ExchangePublicTokenOutputData outputData =
+ plaidInteractor.exchangePublicToken(inputData);
+
+ return ResponseEntity.ok(outputData);
+ }
+}
diff --git a/src/main/java/interface_adapter/signup/SignupController.java b/src/main/java/osiris/interface_adapter/signup/SignupController.java
similarity index 50%
rename from src/main/java/interface_adapter/signup/SignupController.java
rename to src/main/java/osiris/interface_adapter/signup/SignupController.java
index c01d25aa5..2535d512b 100644
--- a/src/main/java/interface_adapter/signup/SignupController.java
+++ b/src/main/java/osiris/interface_adapter/signup/SignupController.java
@@ -1,7 +1,7 @@
-package interface_adapter.signup;
+package osiris.interface_adapter.signup;
-import use_case.signup.SignupInputBoundary;
-import use_case.signup.SignupInputData;
+import osiris.use_case.signup.SignupInputBoundary;
+import osiris.use_case.signup.SignupInputData;
/**
* Controller for the Signup Use Case.
@@ -16,13 +16,14 @@ public SignupController(SignupInputBoundary userSignupUseCaseInteractor) {
/**
* Executes the Signup Use Case.
- * @param username the username to sign up
+ * @param email the username to sign up
* @param password1 the password
* @param password2 the password repeated
+ * @param access_code the access code
*/
- public void execute(String username, String password1, String password2) {
+ public void execute(String email, String password1, String password2, String access_code) {
final SignupInputData signupInputData = new SignupInputData(
- username, password1, password2);
+ email, password1, password2, access_code);
userSignupUseCaseInteractor.execute(signupInputData);
}
@@ -30,7 +31,14 @@ public void execute(String username, String password1, String password2) {
/**
* Executes the "switch to LoginView" Use Case.
*/
- public void switchToLoginView() {
- userSignupUseCaseInteractor.switchToLoginView();
+ public void switchToVerifyView() {
+ userSignupUseCaseInteractor.switchToVerifyView();
+ }
+
+ /**
+ * Executes the "switch to WelcomeView" Use Case.
+ */
+ public void switchToWelcomeView() {
+ userSignupUseCaseInteractor.switchToWelcomeView();
}
}
diff --git a/src/main/java/osiris/interface_adapter/signup/SignupPresenter.java b/src/main/java/osiris/interface_adapter/signup/SignupPresenter.java
new file mode 100644
index 000000000..cd74b9a96
--- /dev/null
+++ b/src/main/java/osiris/interface_adapter/signup/SignupPresenter.java
@@ -0,0 +1,64 @@
+package osiris.interface_adapter.signup;
+
+import osiris.interface_adapter.ViewManagerModel;
+import osiris.interface_adapter.verify.VerifyState;
+import osiris.interface_adapter.verify.VerifyViewModel;
+import osiris.interface_adapter.dashboard.DashboardViewModel;
+
+import osiris.interface_adapter.welcome.WelcomeViewModel;
+import osiris.use_case.signup.SignupOutputBoundary;
+import osiris.use_case.signup.SignupOutputData;
+
+/**
+ * The Presenter for the Signup Use Case.
+ */
+public class SignupPresenter implements SignupOutputBoundary {
+
+ private final SignupViewModel signupViewModel;
+ private final VerifyViewModel verifyViewModel;
+ private final ViewManagerModel viewManagerModel;
+ private final WelcomeViewModel welcomeViewModel;
+
+ public SignupPresenter(ViewManagerModel viewManagerModel,
+ SignupViewModel signupViewModel,
+ VerifyViewModel verifyViewModel, WelcomeViewModel welcomeViewModel,
+ DashboardViewModel dashboardViewModel) {
+ this.viewManagerModel = viewManagerModel;
+ this.signupViewModel = signupViewModel;
+ this.verifyViewModel = verifyViewModel;
+ this.welcomeViewModel = welcomeViewModel;
+ }
+
+ @Override
+ public void prepareSuccessView(SignupOutputData response) {
+ // On success, switch to the login view.
+
+ final VerifyState verifyState = verifyViewModel.getState();
+ verifyState.setUsername(response.getEmail());
+ this.verifyViewModel.setState(verifyState);
+ verifyViewModel.firePropertyChanged();
+
+ viewManagerModel.setState(verifyViewModel.getViewName());
+ viewManagerModel.firePropertyChanged();
+
+ }
+
+ @Override
+ public void prepareFailView(String error) {
+ final SignupState signupState = signupViewModel.getState();
+ signupState.setEmailError(error);
+ signupViewModel.firePropertyChanged();
+ }
+
+ @Override
+ public void switchToVerifyView() {
+ viewManagerModel.setState(verifyViewModel.getViewName());
+ viewManagerModel.firePropertyChanged();
+ }
+
+ @Override
+ public void switchToWelcomeView() {
+ viewManagerModel.setState(welcomeViewModel.getViewName());
+ viewManagerModel.firePropertyChanged();
+ }
+}
diff --git a/src/main/java/interface_adapter/signup/SignupState.java b/src/main/java/osiris/interface_adapter/signup/SignupState.java
similarity index 67%
rename from src/main/java/interface_adapter/signup/SignupState.java
rename to src/main/java/osiris/interface_adapter/signup/SignupState.java
index 4a7f69327..152c4c4f0 100644
--- a/src/main/java/interface_adapter/signup/SignupState.java
+++ b/src/main/java/osiris/interface_adapter/signup/SignupState.java
@@ -1,28 +1,33 @@
-package interface_adapter.signup;
+package osiris.interface_adapter.signup;
/**
* The state for the Signup View Model.
*/
public class SignupState {
- private String username = "";
- private String usernameError;
+ private String email = "";
+ private String emailError;
private String password = "";
private String passwordError;
private String repeatPassword = "";
private String repeatPasswordError;
+ private String access_code = "";
- public String getUsername() {
- return username;
+ public String getEmail() {
+ return email;
}
- public String getUsernameError() {
- return usernameError;
+ public String getEmailError() {
+ return emailError;
}
public String getPassword() {
return password;
}
+ public String getAccessCode() {
+ return access_code;
+ }
+
public String getPasswordError() {
return passwordError;
}
@@ -35,12 +40,12 @@ public String getRepeatPasswordError() {
return repeatPasswordError;
}
- public void setUsername(String username) {
- this.username = username;
+ public void setEmail(String email) {
+ this.email = email;
}
- public void setUsernameError(String usernameError) {
- this.usernameError = usernameError;
+ public void setEmailError(String emailError) {
+ this.emailError = emailError;
}
public void setPassword(String password) {
@@ -62,9 +67,10 @@ public void setRepeatPasswordError(String repeatPasswordError) {
@Override
public String toString() {
return "SignupState{"
- + "username='" + username + '\''
+ + "email='" + email + '\''
+ ", password='" + password + '\''
+ ", repeatPassword='" + repeatPassword + '\''
+ + ", access_code='" + access_code + '\''
+ '}';
}
}
diff --git a/src/main/java/osiris/interface_adapter/signup/SignupViewModel.java b/src/main/java/osiris/interface_adapter/signup/SignupViewModel.java
new file mode 100644
index 000000000..e2901f511
--- /dev/null
+++ b/src/main/java/osiris/interface_adapter/signup/SignupViewModel.java
@@ -0,0 +1,23 @@
+package osiris.interface_adapter.signup;
+
+import osiris.interface_adapter.ViewModel;
+
+/**
+ * The ViewModel for the View.
+ */
+public class SignupViewModel extends ViewModel {
+
+ public static final String TITLE_LABEL = "Create Your Account";
+ public static final String EMAIL_LABEL = "Type Your Email";
+ public static final String PASSWORD_LABEL = "Choose password";
+ public static final String REPEAT_PASSWORD_LABEL = "Enter password again";
+
+ public static final String SIGNUP_BUTTON_LABEL = "Create Account";
+ public static final String RETURN_BUTTON_LABEL = "Go back";
+
+ public SignupViewModel() {
+ super("sign up");
+ setState(new SignupState());
+ }
+
+}
diff --git a/src/main/java/osiris/interface_adapter/verify/VerifyController.java b/src/main/java/osiris/interface_adapter/verify/VerifyController.java
new file mode 100644
index 000000000..088d2d314
--- /dev/null
+++ b/src/main/java/osiris/interface_adapter/verify/VerifyController.java
@@ -0,0 +1,45 @@
+package osiris.interface_adapter.verify;
+
+import osiris.use_case.verify.VerifyInputBoundary;
+import osiris.use_case.verify.VerifyInputData;
+
+/**
+ * Controller for the verify Use Case.
+ */
+
+public class VerifyController {
+ private final VerifyInputBoundary verifyUseCaseInteractor;
+
+ public VerifyController(VerifyInputBoundary verifyUseCaseInteractor) {
+ this.verifyUseCaseInteractor = verifyUseCaseInteractor;
+ }
+
+ /**
+ * Executes the Verify Email Use Case.
+ * @param email the email of the user whose password to change
+ * @param verifyCode the verification code that is being sent to the user.
+ */
+
+ public void execute(String email, String verifyCode) {
+ final VerifyInputData verifyInputData = new VerifyInputData(email, verifyCode);
+ verifyUseCaseInteractor.execute(verifyInputData);
+ }
+
+ /**
+ * Executes the "switch to LoginView" Use Case.
+ */
+
+ public void switchToSignUpView() {
+ verifyUseCaseInteractor.switchToSignUpView();
+ }
+
+ /**
+ * Resends the verification email to the user.
+ * @param email the user's email
+ */
+ public void resendVerificationEmail(String email) {
+ VerifyInputData inputData = new VerifyInputData(email, null);
+ verifyUseCaseInteractor.resendVerificationEmail(inputData);
+ }
+
+}
diff --git a/src/main/java/osiris/interface_adapter/verify/VerifyPresenter.java b/src/main/java/osiris/interface_adapter/verify/VerifyPresenter.java
new file mode 100644
index 000000000..a51c6d964
--- /dev/null
+++ b/src/main/java/osiris/interface_adapter/verify/VerifyPresenter.java
@@ -0,0 +1,43 @@
+package osiris.interface_adapter.verify;
+
+import osiris.interface_adapter.ViewManagerModel;
+import osiris.interface_adapter.signup.SignupViewModel;
+import osiris.use_case.verify.VerifyOutputBoundary;
+import osiris.use_case.verify.VerifyOutputData;
+/**
+ * The Presenter for the Verify Email Use Case.
+ */
+
+public class VerifyPresenter implements VerifyOutputBoundary {
+ private final VerifyViewModel verifyViewModel;
+ private final SignupViewModel signupViewModel;
+ private final ViewManagerModel viewManagerModel;
+
+ public VerifyPresenter(VerifyViewModel verifyViewModel, SignupViewModel signupViewModel,
+ ViewManagerModel viewManagerModel) {
+ this.verifyViewModel = verifyViewModel;
+ this.signupViewModel = signupViewModel;
+ this.viewManagerModel = viewManagerModel;
+ }
+
+ @Override
+ public void prepareSuccessView(VerifyOutputData outputData) {
+ // currently there isn't anything to change based on the output data,
+ // since the output data only contains the username, which remains the same.
+ // We still fire the property changed event, but just to let the view know that
+ // it can alert the user that their password was changed successfully...
+ verifyViewModel.firePropertyChanged("password");
+ }
+
+ @Override
+ public void prepareFailView(String error) {
+ // note: this use case currently can't fail
+ // If we get the wrong code
+ }
+
+ @Override
+ public void switchToSignUpView() {
+ viewManagerModel.setState(signupViewModel.getViewName());
+ viewManagerModel.firePropertyChanged();
+ }
+}
diff --git a/src/main/java/osiris/interface_adapter/verify/VerifyState.java b/src/main/java/osiris/interface_adapter/verify/VerifyState.java
new file mode 100644
index 000000000..b29fbd499
--- /dev/null
+++ b/src/main/java/osiris/interface_adapter/verify/VerifyState.java
@@ -0,0 +1,30 @@
+package osiris.interface_adapter.verify;
+
+/**
+ * The State information representing the logged-in user.
+ */
+public class VerifyState {
+ private String username = "";
+ private String verifyCodeError;
+
+ public VerifyState(VerifyState copy) {
+ username = copy.username;
+ verifyCodeError = copy.verifyCodeError;
+ }
+ // Because of the previous copy constructor, the default constructor must be explicit.
+
+ public VerifyState() {
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setVerifyCodeError(String verifyCodeError) {
+ this.verifyCodeError = verifyCodeError;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+}
diff --git a/src/main/java/osiris/interface_adapter/verify/VerifyViewModel.java b/src/main/java/osiris/interface_adapter/verify/VerifyViewModel.java
new file mode 100644
index 000000000..18a51f520
--- /dev/null
+++ b/src/main/java/osiris/interface_adapter/verify/VerifyViewModel.java
@@ -0,0 +1,20 @@
+package osiris.interface_adapter.verify;
+
+import osiris.interface_adapter.ViewModel;
+
+/**
+ * The View Model for the Logged In View.
+ */
+
+public class VerifyViewModel extends ViewModel {
+ public static final String TITLE_LABEL = "Verify Your Account";
+ public static final String VERIFY_LABEL = "Enter the Code: ";
+ public static final String VERIFY_BUTTON_LABEL = "Verify";
+ public static final String RETURN_BUTTON_LABEL = "Go Back";
+
+ public VerifyViewModel() {
+ super("verify");
+ setState(new VerifyState());
+ }
+
+}
diff --git a/src/main/java/osiris/interface_adapter/viewexpenses/ViewExpensesController.java b/src/main/java/osiris/interface_adapter/viewexpenses/ViewExpensesController.java
new file mode 100644
index 000000000..e4078483e
--- /dev/null
+++ b/src/main/java/osiris/interface_adapter/viewexpenses/ViewExpensesController.java
@@ -0,0 +1,32 @@
+package osiris.interface_adapter.viewexpenses;
+
+import osiris.use_case.viewexpenses.ViewExpensesInputBoundary;
+import osiris.use_case.viewexpenses.ViewExpensesInputData;
+
+/**
+ * The controller for the ViewExpenses Use Case.
+ */
+public class ViewExpensesController {
+ private final ViewExpensesInputBoundary interactor;
+
+ public ViewExpensesController(ViewExpensesInputBoundary interactor) {
+ this.interactor = interactor;
+ }
+
+ /**
+ * Executes the ViewExpenses Use Case.
+ * @param transactionList List of transactions for a given access token
+ */
+
+ public void execute(String transactionList) {
+ final ViewExpensesInputData inputData = new ViewExpensesInputData(transactionList);
+ interactor.execute(inputData);
+ }
+
+ /**
+ * Executes the "switch to Home" Use Case.
+ */
+ public void switchToHomeView() {
+ interactor.switchToHomeView();
+ }
+}
diff --git a/src/main/java/osiris/interface_adapter/viewexpenses/ViewExpensesPresenter.java b/src/main/java/osiris/interface_adapter/viewexpenses/ViewExpensesPresenter.java
new file mode 100644
index 000000000..c05ecd6a6
--- /dev/null
+++ b/src/main/java/osiris/interface_adapter/viewexpenses/ViewExpensesPresenter.java
@@ -0,0 +1,40 @@
+package osiris.interface_adapter.viewexpenses;
+
+import osiris.interface_adapter.ViewManagerModel;
+import osiris.interface_adapter.dashboard.DashboardViewModel;
+import osiris.use_case.viewexpenses.ViewExpensesOutputBoundary;
+import osiris.use_case.viewexpenses.ViewExpensesOutputData;
+
+/**
+ * The Presenter for the ViewExpenses Use Case.
+ */
+public class ViewExpensesPresenter implements ViewExpensesOutputBoundary {
+ private final ViewExpensesViewModel viewExpensesViewModel;
+ private final ViewManagerModel viewManagerModel;
+ private DashboardViewModel dashboardViewModel;
+
+ public ViewExpensesPresenter(ViewExpensesViewModel viewExpensesViewModel,
+ ViewManagerModel viewManagerModel) {
+ this.viewExpensesViewModel = viewExpensesViewModel;
+ this.viewManagerModel = viewManagerModel;
+ }
+
+ @Override
+ public void prepareChart(ViewExpensesOutputData outputData) {
+ final ViewExpensesState viewExpensesState = viewExpensesViewModel.getState();
+ viewExpensesState.setEssential(outputData.getEssentialTotal());
+ viewExpensesState.setNonEssential(outputData.getNonEssentialTotal());
+ viewExpensesViewModel.setState(viewExpensesState);
+ viewManagerModel.setState(viewExpensesViewModel.getViewName());
+ viewManagerModel.firePropertyChanged();
+ // System.out.println("Essential Amount: " + outputData.getEssentialTotal());
+ // System.out.println("Non-Essential Amount: " + outputData.getNonEssentialTotal());
+ }
+
+ @Override
+ public void switchToHomeView() {
+ viewManagerModel.setState("dashboard");
+ viewManagerModel.firePropertyChanged();
+ }
+
+}
diff --git a/src/main/java/osiris/interface_adapter/viewexpenses/ViewExpensesState.java b/src/main/java/osiris/interface_adapter/viewexpenses/ViewExpensesState.java
new file mode 100644
index 000000000..eced7ec08
--- /dev/null
+++ b/src/main/java/osiris/interface_adapter/viewexpenses/ViewExpensesState.java
@@ -0,0 +1,26 @@
+package osiris.interface_adapter.viewexpenses;
+
+/**
+ * The state for the ViewExpenses Model.
+ */
+
+public class ViewExpensesState {
+ private double essential;
+ private double nonEssential;
+
+ public double getEssential() {
+ return essential;
+ }
+
+ public double getNonEssential() {
+ return nonEssential;
+ }
+
+ public void setEssential(double essential) {
+ this.essential = essential;
+ }
+
+ public void setNonEssential(double nonEssential) {
+ this.nonEssential = nonEssential;
+ }
+}
diff --git a/src/main/java/osiris/interface_adapter/viewexpenses/ViewExpensesViewModel.java b/src/main/java/osiris/interface_adapter/viewexpenses/ViewExpensesViewModel.java
new file mode 100644
index 000000000..31ecf3d5e
--- /dev/null
+++ b/src/main/java/osiris/interface_adapter/viewexpenses/ViewExpensesViewModel.java
@@ -0,0 +1,19 @@
+package osiris.interface_adapter.viewexpenses;
+
+import osiris.interface_adapter.ViewModel;
+
+/**
+ * The ViewModel for the ViewExpenses.
+ */
+public class ViewExpensesViewModel extends ViewModel {
+ public static final String TITLE_LABEL = "Essentials vs Non-Essentials";
+ public static final String BUTTON_LABEL = "Chart";
+ public static final String BACK_LABEL = "Back";
+
+ public ViewExpensesViewModel() {
+
+ super("view expenses");
+ setState(new ViewExpensesState());
+ }
+
+}
diff --git a/src/main/java/osiris/interface_adapter/welcome/WelcomeController.java b/src/main/java/osiris/interface_adapter/welcome/WelcomeController.java
new file mode 100644
index 000000000..4c08fb5d9
--- /dev/null
+++ b/src/main/java/osiris/interface_adapter/welcome/WelcomeController.java
@@ -0,0 +1,29 @@
+package osiris.interface_adapter.welcome;
+
+import osiris.use_case.welcome.WelcomeInputBoundary;
+
+/**
+ * Controller for the Welcome Use Case.
+ */
+public class WelcomeController {
+
+ private final WelcomeInputBoundary userWelcomeUseCaseInteractor;
+
+ public WelcomeController(WelcomeInputBoundary userWelcomeUseCaseInteractor) {
+ this.userWelcomeUseCaseInteractor = userWelcomeUseCaseInteractor;
+ }
+
+ /**
+ * Executes the "switch to LoginView" Use Case.
+ */
+ public void switchToLoginView() {
+ userWelcomeUseCaseInteractor.switchToLoginView();
+ }
+
+ /**
+ * Executes the "switch to SignUpView" Use Case.
+ */
+ public void switchToSignUpView() {
+ userWelcomeUseCaseInteractor.switchToSignUpView();
+ }
+}
diff --git a/src/main/java/osiris/interface_adapter/welcome/WelcomePresenter.java b/src/main/java/osiris/interface_adapter/welcome/WelcomePresenter.java
new file mode 100644
index 000000000..57a1aeb3b
--- /dev/null
+++ b/src/main/java/osiris/interface_adapter/welcome/WelcomePresenter.java
@@ -0,0 +1,38 @@
+package osiris.interface_adapter.welcome;
+
+import osiris.interface_adapter.ViewManagerModel;
+import osiris.interface_adapter.login.LoginViewModel;
+import osiris.interface_adapter.signup.SignupViewModel;
+import osiris.use_case.welcome.WelcomeOutputBoundary;
+
+/**
+ * Presenter for the Welcome Use Case.
+ */
+public class WelcomePresenter implements WelcomeOutputBoundary {
+
+ private final ViewManagerModel viewManagerModel;
+ private final LoginViewModel loginViewModel;
+ private final SignupViewModel signupViewModel;
+ private final WelcomeViewModel welcomeViewModel;
+
+ public WelcomePresenter(ViewManagerModel viewManagerModel,
+ WelcomeViewModel welcomeViewModel,
+ LoginViewModel loginViewModel, SignupViewModel signupViewModel) {
+ this.viewManagerModel = viewManagerModel;
+ this.welcomeViewModel = welcomeViewModel;
+ this.loginViewModel = loginViewModel;
+ this.signupViewModel = signupViewModel;
+ }
+
+ @Override
+ public void switchToLoginView() {
+ viewManagerModel.setState(loginViewModel.getViewName());
+ viewManagerModel.firePropertyChanged();
+ }
+
+ @Override
+ public void switchToSignUpView() {
+ viewManagerModel.setState(signupViewModel.getViewName());
+ viewManagerModel.firePropertyChanged();
+ }
+}
diff --git a/src/main/java/osiris/interface_adapter/welcome/WelcomeViewModel.java b/src/main/java/osiris/interface_adapter/welcome/WelcomeViewModel.java
new file mode 100644
index 000000000..a3067d48c
--- /dev/null
+++ b/src/main/java/osiris/interface_adapter/welcome/WelcomeViewModel.java
@@ -0,0 +1,20 @@
+package osiris.interface_adapter.welcome;
+
+/**
+ * The ViewModel for the Welcome View.
+ */
+public class WelcomeViewModel {
+
+ public static final String TITLE_LABEL = "Welcome to Osiris!";
+ public static final String SLOGAN_LABEL = "Your path to financial freedom";
+
+ public static final String CREATE_ACCOUNT_BUTTON_LABEL = "Create Account";
+ public static final String LOGIN_BUTTON_LABEL = "Login";
+
+ public WelcomeViewModel() {
+ }
+
+ public String getViewName() {
+ return "welcome";
+ }
+}
diff --git a/src/main/java/osiris/use_case/budget/BudgetInputBoundary.java b/src/main/java/osiris/use_case/budget/BudgetInputBoundary.java
new file mode 100644
index 000000000..93537ff0b
--- /dev/null
+++ b/src/main/java/osiris/use_case/budget/BudgetInputBoundary.java
@@ -0,0 +1,20 @@
+package osiris.use_case.budget;
+
+
+/**
+ * The ImputBoundary for the budget.
+ */
+
+public interface BudgetInputBoundary {
+ /**
+ * Processes the input data for budget.
+ *
+ * @param inputData Input data containing the required information for the use case.
+ */
+ void execute(BudgetInputData inputData);
+
+ /**
+ * Executes the switch to dasboard.
+ */
+ void switchToDashboardView();
+}
diff --git a/src/main/java/osiris/use_case/budget/BudgetInputData.java b/src/main/java/osiris/use_case/budget/BudgetInputData.java
new file mode 100644
index 000000000..d741e59a1
--- /dev/null
+++ b/src/main/java/osiris/use_case/budget/BudgetInputData.java
@@ -0,0 +1,16 @@
+package osiris.use_case.budget;
+
+import lombok.Getter;
+
+/**
+ * The InputData for the budget.
+ */
+@Getter
+public class BudgetInputData {
+ private final String transactionList;
+
+ public BudgetInputData(String transactionList) {
+ this.transactionList = transactionList;
+ }
+
+}
diff --git a/src/main/java/osiris/use_case/budget/BudgetInteractor.java b/src/main/java/osiris/use_case/budget/BudgetInteractor.java
new file mode 100644
index 000000000..80f0e2e4c
--- /dev/null
+++ b/src/main/java/osiris/use_case/budget/BudgetInteractor.java
@@ -0,0 +1,63 @@
+package osiris.use_case.budget;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * The Budget Interactor.
+ */
+public class BudgetInteractor implements BudgetInputBoundary {
+ private final BudgetOutputBoundary presenter;
+
+ public BudgetInteractor(BudgetOutputBoundary presenter) {
+ this.presenter = presenter;
+ }
+
+ @Override
+ public void execute(BudgetInputData inputData) {
+ // Fetch transactions from Plaid API
+ final String transactionsJson = inputData.getTransactionList();
+
+ double jan = 0.0;
+ double feb = 0.0;
+ double mar = 0.0;
+ double apr = 0.0;
+ double may = 0.0;
+ double jun = 0.0;
+ double jul = 0.0;
+ double aug = 0.0;
+ double sep = 0.0;
+ double oct = 0.0;
+ double nov = 0.0;
+ double dec = 0.0;
+ ArrayList monthlyTotals = new ArrayList<>(Arrays.asList(jan, feb, mar, apr, may, jun, jul, aug,
+ sep, oct, nov, dec));
+
+ // Categorize transactions into months
+ final JsonObject jsonResponse = JsonParser.parseString(transactionsJson).getAsJsonObject();
+ final JsonArray transactions = jsonResponse.getAsJsonArray("transactions");
+
+ for (JsonElement transactionElement : transactions) {
+ final JsonObject transaction = transactionElement.getAsJsonObject();
+ final double amount = transaction.get("amount").getAsDouble();
+ final String strDate = transaction.get("date").getAsString();
+ final int monthIndex = Integer.parseInt(strDate.substring(5, 7));
+
+ double newAmount = monthlyTotals.get(monthIndex) + amount;
+ monthlyTotals.set(monthIndex, newAmount);
+ }
+
+ final BudgetOutputData outputData = new BudgetOutputData(monthlyTotals);
+
+ }
+
+ @Override
+ public void switchToDashboardView() {
+ presenter.switchToDashboardView();
+ }
+}
diff --git a/src/main/java/osiris/use_case/budget/BudgetOutputBoundary.java b/src/main/java/osiris/use_case/budget/BudgetOutputBoundary.java
new file mode 100644
index 000000000..f311aa869
--- /dev/null
+++ b/src/main/java/osiris/use_case/budget/BudgetOutputBoundary.java
@@ -0,0 +1,13 @@
+package osiris.use_case.budget;
+
+
+/**
+ * Budget OutputBoundary.
+ */
+public interface BudgetOutputBoundary {
+
+ /**
+ * Executes the switch to the dashboard use case.
+ */
+ void switchToDashboardView();
+}
diff --git a/src/main/java/osiris/use_case/budget/BudgetOutputData.java b/src/main/java/osiris/use_case/budget/BudgetOutputData.java
new file mode 100644
index 000000000..2057ccbdb
--- /dev/null
+++ b/src/main/java/osiris/use_case/budget/BudgetOutputData.java
@@ -0,0 +1,18 @@
+package osiris.use_case.budget;
+
+import java.util.ArrayList;
+
+/**
+ * ViewExpenses Output Data.
+ */
+public class BudgetOutputData {
+ private final ArrayList monthlyTotals;
+
+ public BudgetOutputData(ArrayList monthlyTotals) {
+ this.monthlyTotals = monthlyTotals;
+ }
+
+ public ArrayList getMonthlyTotals() {
+ return monthlyTotals;
+ }
+}
diff --git a/src/main/java/use_case/change_password/ChangePasswordInputBoundary.java b/src/main/java/osiris/use_case/change_password/ChangePasswordInputBoundary.java
similarity index 87%
rename from src/main/java/use_case/change_password/ChangePasswordInputBoundary.java
rename to src/main/java/osiris/use_case/change_password/ChangePasswordInputBoundary.java
index 06ae9448e..3b423ee0d 100644
--- a/src/main/java/use_case/change_password/ChangePasswordInputBoundary.java
+++ b/src/main/java/osiris/use_case/change_password/ChangePasswordInputBoundary.java
@@ -1,4 +1,4 @@
-package use_case.change_password;
+package osiris.use_case.change_password;
/**
* The Change Password Use Case.
diff --git a/src/main/java/osiris/use_case/change_password/ChangePasswordInputData.java b/src/main/java/osiris/use_case/change_password/ChangePasswordInputData.java
new file mode 100644
index 000000000..3889caec5
--- /dev/null
+++ b/src/main/java/osiris/use_case/change_password/ChangePasswordInputData.java
@@ -0,0 +1,30 @@
+package osiris.use_case.change_password;
+
+/**
+ * The input data for the Change Password Use Case.
+ */
+public class ChangePasswordInputData {
+
+ private final String password;
+ private final String email;
+ private final String access_code;
+
+ public ChangePasswordInputData(String password, String username, String access_code) {
+ this.password = password;
+ this.email = username;
+ this.access_code = access_code;
+ }
+
+ String getPassword() {
+ return password;
+ }
+
+ String getEmail() {
+ return email;
+ }
+
+ String getAccessCode() {
+ return access_code;
+ }
+
+}
diff --git a/src/main/java/use_case/change_password/ChangePasswordInteractor.java b/src/main/java/osiris/use_case/change_password/ChangePasswordInteractor.java
similarity index 83%
rename from src/main/java/use_case/change_password/ChangePasswordInteractor.java
rename to src/main/java/osiris/use_case/change_password/ChangePasswordInteractor.java
index df91196c1..1389e1cff 100644
--- a/src/main/java/use_case/change_password/ChangePasswordInteractor.java
+++ b/src/main/java/osiris/use_case/change_password/ChangePasswordInteractor.java
@@ -1,7 +1,7 @@
-package use_case.change_password;
+package osiris.use_case.change_password;
-import entity.User;
-import entity.UserFactory;
+import osiris.entity.User;
+import osiris.entity.UserFactory;
/**
* The Change Password Interactor.
@@ -21,11 +21,12 @@ public ChangePasswordInteractor(ChangePasswordUserDataAccessInterface changePass
@Override
public void execute(ChangePasswordInputData changePasswordInputData) {
- final User user = userFactory.create(changePasswordInputData.getUsername(),
- changePasswordInputData.getPassword());
+ final User user = userFactory.create(changePasswordInputData.getEmail(),
+ changePasswordInputData.getPassword(),
+ changePasswordInputData.getAccessCode());
userDataAccessObject.changePassword(user);
- final ChangePasswordOutputData changePasswordOutputData = new ChangePasswordOutputData(user.getName(),
+ final ChangePasswordOutputData changePasswordOutputData = new ChangePasswordOutputData(user.getEmail(),
false);
userPresenter.prepareSuccessView(changePasswordOutputData);
}
diff --git a/src/main/java/use_case/change_password/ChangePasswordOutputBoundary.java b/src/main/java/osiris/use_case/change_password/ChangePasswordOutputBoundary.java
similarity index 92%
rename from src/main/java/use_case/change_password/ChangePasswordOutputBoundary.java
rename to src/main/java/osiris/use_case/change_password/ChangePasswordOutputBoundary.java
index fce28367b..6781ff1ba 100644
--- a/src/main/java/use_case/change_password/ChangePasswordOutputBoundary.java
+++ b/src/main/java/osiris/use_case/change_password/ChangePasswordOutputBoundary.java
@@ -1,4 +1,4 @@
-package use_case.change_password;
+package osiris.use_case.change_password;
/**
* The output boundary for the Change Password Use Case.
diff --git a/src/main/java/use_case/change_password/ChangePasswordOutputData.java b/src/main/java/osiris/use_case/change_password/ChangePasswordOutputData.java
similarity index 53%
rename from src/main/java/use_case/change_password/ChangePasswordOutputData.java
rename to src/main/java/osiris/use_case/change_password/ChangePasswordOutputData.java
index b47b83fbc..daa0ebc9f 100644
--- a/src/main/java/use_case/change_password/ChangePasswordOutputData.java
+++ b/src/main/java/osiris/use_case/change_password/ChangePasswordOutputData.java
@@ -1,21 +1,21 @@
-package use_case.change_password;
+package osiris.use_case.change_password;
/**
* Output Data for the Change Password Use Case.
*/
public class ChangePasswordOutputData {
- private final String username;
+ private final String email;
private final boolean useCaseFailed;
- public ChangePasswordOutputData(String username, boolean useCaseFailed) {
- this.username = username;
+ public ChangePasswordOutputData(String email, boolean useCaseFailed) {
+ this.email = email;
this.useCaseFailed = useCaseFailed;
}
- public String getUsername() {
- return username;
+ public String getEmail() {
+ return email;
}
public boolean isUseCaseFailed() {
diff --git a/src/main/java/use_case/change_password/ChangePasswordUserDataAccessInterface.java b/src/main/java/osiris/use_case/change_password/ChangePasswordUserDataAccessInterface.java
similarity index 81%
rename from src/main/java/use_case/change_password/ChangePasswordUserDataAccessInterface.java
rename to src/main/java/osiris/use_case/change_password/ChangePasswordUserDataAccessInterface.java
index 6b73ab6b0..89d398cfd 100644
--- a/src/main/java/use_case/change_password/ChangePasswordUserDataAccessInterface.java
+++ b/src/main/java/osiris/use_case/change_password/ChangePasswordUserDataAccessInterface.java
@@ -1,6 +1,6 @@
-package use_case.change_password;
+package osiris.use_case.change_password;
-import entity.User;
+import osiris.entity.User;
/**
* The interface of the DAO for the Change Password Use Case.
diff --git a/src/main/java/osiris/use_case/dashboard/DashboardInputBoundary.java b/src/main/java/osiris/use_case/dashboard/DashboardInputBoundary.java
new file mode 100644
index 000000000..80b6f96ab
--- /dev/null
+++ b/src/main/java/osiris/use_case/dashboard/DashboardInputBoundary.java
@@ -0,0 +1,22 @@
+package osiris.use_case.dashboard;
+
+/**
+ * Input boundary for the Dashboard Use Case.
+ */
+public interface DashboardInputBoundary {
+
+ /**
+ * Executes the switch to add bank account view use case.
+ */
+ void switchToAddBankAccountView();
+
+ /**
+ * Executes the switch to ViewExpenses view use case.
+ */
+ void switchToViewExpenses();
+
+ /**
+ * Executes the switch to budget view use case.
+ */
+ void switchToBudgetView();
+}
diff --git a/src/main/java/osiris/use_case/dashboard/DashboardInteractor.java b/src/main/java/osiris/use_case/dashboard/DashboardInteractor.java
new file mode 100644
index 000000000..3678fff61
--- /dev/null
+++ b/src/main/java/osiris/use_case/dashboard/DashboardInteractor.java
@@ -0,0 +1,30 @@
+package osiris.use_case.dashboard;
+
+import osiris.entity.UserFactory;
+
+/**
+ * Interactor for the Dashboard Use Case.
+ */
+public class DashboardInteractor implements DashboardInputBoundary {
+
+ private final UserFactory userFactory;
+ private final DashboardOutputBoundary userPresenter;
+
+ public DashboardInteractor(DashboardOutputBoundary dashboardOutputBoundary, UserFactory userFactory) {
+ this.userPresenter = dashboardOutputBoundary;
+ this.userFactory = userFactory;
+ }
+
+ @Override
+ public void switchToAddBankAccountView() {
+ userPresenter.switchToAddBankAccountView();
+ }
+
+ @Override
+ public void switchToViewExpenses() {
+ userPresenter.switchToViewExpenses();
+ }
+
+ @Override
+ public void switchToBudgetView() { userPresenter.switchToBudgetView(); }
+}
diff --git a/src/main/java/osiris/use_case/dashboard/DashboardOutputBoundary.java b/src/main/java/osiris/use_case/dashboard/DashboardOutputBoundary.java
new file mode 100644
index 000000000..79c716445
--- /dev/null
+++ b/src/main/java/osiris/use_case/dashboard/DashboardOutputBoundary.java
@@ -0,0 +1,22 @@
+package osiris.use_case.dashboard;
+
+/**
+ * Output boundary for the Dashboard Use Case.
+ */
+public interface DashboardOutputBoundary {
+
+ /**
+ * Switches to the add bank account View.
+ */
+ void switchToAddBankAccountView();
+
+ /**
+ * Switches to the viewExpenses view.
+ */
+ void switchToViewExpenses();
+
+ /**
+ * Switches to the budget View.
+ */
+ void switchToBudgetView();
+}
diff --git a/src/main/java/osiris/use_case/grabtransactions/GrabTransactionOutputData.java b/src/main/java/osiris/use_case/grabtransactions/GrabTransactionOutputData.java
new file mode 100644
index 000000000..e4f6f13a4
--- /dev/null
+++ b/src/main/java/osiris/use_case/grabtransactions/GrabTransactionOutputData.java
@@ -0,0 +1,24 @@
+package osiris.use_case.grabtransactions;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * The GrabTransactions Output Data.
+ */
+
+@Setter
+@Getter
+public class GrabTransactionOutputData {
+
+ private String transactions;
+
+ public GrabTransactionOutputData(String transactions) {
+ this.transactions = transactions;
+ }
+
+ public String getTransactions() {
+ return transactions;
+ }
+
+}
diff --git a/src/main/java/osiris/use_case/grabtransactions/GrabTransactionUserDataAccessInterface.java b/src/main/java/osiris/use_case/grabtransactions/GrabTransactionUserDataAccessInterface.java
new file mode 100644
index 000000000..e6ce40cd8
--- /dev/null
+++ b/src/main/java/osiris/use_case/grabtransactions/GrabTransactionUserDataAccessInterface.java
@@ -0,0 +1,17 @@
+package osiris.use_case.grabtransactions;
+
+import osiris.entity.User;
+
+/**
+ * The GrabTransactions DataAccessInterface.
+ */
+
+public interface GrabTransactionUserDataAccessInterface {
+ /**
+ * Get method for username.
+ * @param username the username to look for
+ * @return the code
+ */
+
+ User get(String username);
+}
diff --git a/src/main/java/osiris/use_case/grabtransactions/GrabTransactionsInputBoundary.java b/src/main/java/osiris/use_case/grabtransactions/GrabTransactionsInputBoundary.java
new file mode 100644
index 000000000..729ce7023
--- /dev/null
+++ b/src/main/java/osiris/use_case/grabtransactions/GrabTransactionsInputBoundary.java
@@ -0,0 +1,21 @@
+package osiris.use_case.grabtransactions;
+
+import java.io.IOException;
+
+import osiris.utility.exceptions.PlaidUseCaseException;
+
+/**
+ * The GrabTransactions Input Boundary.
+ */
+public interface GrabTransactionsInputBoundary {
+ /**
+ * Execute the View Expenses Use Case.
+ * @param grabTransactionsInputData the input data for this use case
+ * @return GrabTransactionOutputData containing the List of Transactions
+ * @throws IOException when performing input/output operation
+ * @throws PlaidUseCaseException when performing Plaid call
+ */
+
+ GrabTransactionOutputData fetchTransactions(GrabTransactionsInputData grabTransactionsInputData)
+ throws IOException, PlaidUseCaseException;
+}
diff --git a/src/main/java/osiris/use_case/grabtransactions/GrabTransactionsInputData.java b/src/main/java/osiris/use_case/grabtransactions/GrabTransactionsInputData.java
new file mode 100644
index 000000000..e3a149cd3
--- /dev/null
+++ b/src/main/java/osiris/use_case/grabtransactions/GrabTransactionsInputData.java
@@ -0,0 +1,18 @@
+package osiris.use_case.grabtransactions;
+
+/**
+ * The GrabTransactions Output Data.
+ */
+public class GrabTransactionsInputData {
+
+ // private final String access_code;
+ private final String username;
+
+ public GrabTransactionsInputData(String username) {
+ this.username = username;
+ }
+
+ String getUsername() {
+ return username;
+ }
+}
diff --git a/src/main/java/osiris/use_case/grabtransactions/GrabTransactionsInteractor.java b/src/main/java/osiris/use_case/grabtransactions/GrabTransactionsInteractor.java
new file mode 100644
index 000000000..3b48f78e8
--- /dev/null
+++ b/src/main/java/osiris/use_case/grabtransactions/GrabTransactionsInteractor.java
@@ -0,0 +1,44 @@
+package osiris.use_case.grabtransactions;
+
+import java.io.IOException;
+
+import osiris.data_access.PlaidDataAccessObject;
+import osiris.entity.User;
+import osiris.utility.exceptions.PlaidUseCaseException;
+
+/**
+ * The GrabTransaction Interactor.
+ */
+public class GrabTransactionsInteractor implements GrabTransactionsInputBoundary {
+ private final GrabTransactionUserDataAccessInterface userDataAccessObject;
+ private final PlaidDataAccessObject plaidDao;
+
+ public GrabTransactionsInteractor(GrabTransactionUserDataAccessInterface userDataAccessObject,
+ PlaidDataAccessObject plaidDao) {
+ this.userDataAccessObject = userDataAccessObject;
+ this.plaidDao = plaidDao;
+ }
+
+ @Override
+ public GrabTransactionOutputData fetchTransactions(GrabTransactionsInputData grabTransactionsInputData)
+ throws IOException, PlaidUseCaseException {
+ try {
+ final User user = userDataAccessObject.get(grabTransactionsInputData.getUsername());
+ if (user == null) {
+ throw new PlaidUseCaseException("User not found: " + grabTransactionsInputData.getUsername());
+ }
+ final String token = user.getAccessCode();
+
+ final String transactions = plaidDao.fetchTransactions(token);
+ return new GrabTransactionOutputData(transactions);
+
+ }
+ catch (PlaidUseCaseException ex) {
+ throw new PlaidUseCaseException(ex.getMessage(), ex);
+ }
+ catch (IOException ex) {
+ throw new IOException("IO Error while creating transactions", ex);
+ }
+ }
+
+}
diff --git a/src/main/java/osiris/use_case/grabtransactions/GrabTransactionsOutputBoundary.java b/src/main/java/osiris/use_case/grabtransactions/GrabTransactionsOutputBoundary.java
new file mode 100644
index 000000000..df5973fbe
--- /dev/null
+++ b/src/main/java/osiris/use_case/grabtransactions/GrabTransactionsOutputBoundary.java
@@ -0,0 +1,13 @@
+package osiris.use_case.grabtransactions;
+
+/**
+ * Output Boundary for GrabTransactions UseCase.
+ */
+public interface GrabTransactionsOutputBoundary {
+ /**
+ * Presents the transaction data output to the user/system.
+ *
+ * @param outputData The processed output data containing transaction information.
+ */
+ void presentTransactions(GrabTransactionOutputData outputData);
+}
diff --git a/src/main/java/use_case/login/LoginInputBoundary.java b/src/main/java/osiris/use_case/login/LoginInputBoundary.java
similarity index 80%
rename from src/main/java/use_case/login/LoginInputBoundary.java
rename to src/main/java/osiris/use_case/login/LoginInputBoundary.java
index faf72dc96..b8d53279d 100644
--- a/src/main/java/use_case/login/LoginInputBoundary.java
+++ b/src/main/java/osiris/use_case/login/LoginInputBoundary.java
@@ -1,4 +1,4 @@
-package use_case.login;
+package osiris.use_case.login;
/**
* Input Boundary for actions which are related to logging in.
@@ -10,4 +10,6 @@ public interface LoginInputBoundary {
* @param loginInputData the input data
*/
void execute(LoginInputData loginInputData);
+
+ void switchToWelcomeView();
}
diff --git a/src/main/java/osiris/use_case/login/LoginInputData.java b/src/main/java/osiris/use_case/login/LoginInputData.java
new file mode 100644
index 000000000..1ed01e418
--- /dev/null
+++ b/src/main/java/osiris/use_case/login/LoginInputData.java
@@ -0,0 +1,30 @@
+package osiris.use_case.login;
+
+/**
+ * The Input Data for the Login Use Case.
+ */
+public class LoginInputData {
+
+ private final String email;
+ private final String password;
+ private final String access_code;
+
+ public LoginInputData(String email, String password, String access_code) {
+ this.email = email;
+ this.password = password;
+ this.access_code = access_code;
+ }
+
+ String getEmail() {
+ return email;
+ }
+
+ String getPassword() {
+ return password;
+ }
+
+ String getAccessCode() {
+ return access_code;
+ }
+
+}
diff --git a/src/main/java/use_case/login/LoginInteractor.java b/src/main/java/osiris/use_case/login/LoginInteractor.java
similarity index 51%
rename from src/main/java/use_case/login/LoginInteractor.java
rename to src/main/java/osiris/use_case/login/LoginInteractor.java
index 5b36ddcd8..a91381167 100644
--- a/src/main/java/use_case/login/LoginInteractor.java
+++ b/src/main/java/osiris/use_case/login/LoginInteractor.java
@@ -1,6 +1,13 @@
-package use_case.login;
+package osiris.use_case.login;
-import entity.User;
+import osiris.entity.User;
+import osiris.entity.UserFactory;
+import osiris.use_case.plaid.PlaidDataBaseUserAccessObjectInterface;
+import osiris.use_case.plaid.UserPlaidDataAccessInterface;
+
+import java.awt.*;
+import java.net.URI;
+import java.net.URLEncoder;
/**
* The Login Interactor.
@@ -10,14 +17,16 @@ public class LoginInteractor implements LoginInputBoundary {
private final LoginOutputBoundary loginPresenter;
public LoginInteractor(LoginUserDataAccessInterface userDataAccessInterface,
- LoginOutputBoundary loginOutputBoundary) {
+ LoginOutputBoundary loginOutputBoundary, UserFactory userFactory,
+ UserPlaidDataAccessInterface plaidDataAccessObject,
+ PlaidDataBaseUserAccessObjectInterface plaidDataBaseUserAccessObjectInterface) {
this.userDataAccessObject = userDataAccessInterface;
this.loginPresenter = loginOutputBoundary;
}
@Override
public void execute(LoginInputData loginInputData) {
- final String username = loginInputData.getUsername();
+ final String username = loginInputData.getEmail();
final String password = loginInputData.getPassword();
if (!userDataAccessObject.existsByName(username)) {
loginPresenter.prepareFailView(username + ": Account does not exist.");
@@ -28,13 +37,25 @@ public void execute(LoginInputData loginInputData) {
loginPresenter.prepareFailView("Incorrect password for \"" + username + "\".");
}
else {
+ final User user = userDataAccessObject.get(loginInputData.getEmail());
- final User user = userDataAccessObject.get(loginInputData.getUsername());
-
- userDataAccessObject.setCurrentUsername(user.getName());
- final LoginOutputData loginOutputData = new LoginOutputData(user.getName(), false);
+ userDataAccessObject.setCurrentEmail(user.getEmail());
+ final LoginOutputData loginOutputData = new LoginOutputData(user.getEmail(), false);
+ try {
+ final String encodedEmail = URLEncoder.encode(user.getEmail(), "UTF-8");
+ final String url = "http://localhost:8080?email=" + encodedEmail;
+ Desktop.getDesktop().browse(new URI(url));
+ }
+ catch (Exception ex) {
+ ex.printStackTrace();
+ }
loginPresenter.prepareSuccessView(loginOutputData);
}
}
}
+
+ @Override
+ public void switchToWelcomeView() {
+ loginPresenter.switchToWelcomeView();
+ }
}
diff --git a/src/main/java/use_case/login/LoginOutputBoundary.java b/src/main/java/osiris/use_case/login/LoginOutputBoundary.java
similarity index 87%
rename from src/main/java/use_case/login/LoginOutputBoundary.java
rename to src/main/java/osiris/use_case/login/LoginOutputBoundary.java
index 08bc4731f..66cdc184a 100644
--- a/src/main/java/use_case/login/LoginOutputBoundary.java
+++ b/src/main/java/osiris/use_case/login/LoginOutputBoundary.java
@@ -1,4 +1,4 @@
-package use_case.login;
+package osiris.use_case.login;
/**
* The output boundary for the Login Use Case.
@@ -15,4 +15,6 @@ public interface LoginOutputBoundary {
* @param errorMessage the explanation of the failure
*/
void prepareFailView(String errorMessage);
+
+ void switchToWelcomeView();
}
diff --git a/src/main/java/osiris/use_case/login/LoginOutputData.java b/src/main/java/osiris/use_case/login/LoginOutputData.java
new file mode 100644
index 000000000..c1c9dfafa
--- /dev/null
+++ b/src/main/java/osiris/use_case/login/LoginOutputData.java
@@ -0,0 +1,20 @@
+package osiris.use_case.login;
+
+/**
+ * Output Data for the Login Use Case.
+ */
+public class LoginOutputData {
+
+ private final String email;
+ private final boolean useCaseFailed;
+
+ public LoginOutputData(String email, boolean useCaseFailed) {
+ this.email = email;
+ this.useCaseFailed = useCaseFailed;
+ }
+
+ public String getEmail() {
+ return email;
+ }
+
+}
diff --git a/src/main/java/use_case/login/LoginUserDataAccessInterface.java b/src/main/java/osiris/use_case/login/LoginUserDataAccessInterface.java
similarity index 72%
rename from src/main/java/use_case/login/LoginUserDataAccessInterface.java
rename to src/main/java/osiris/use_case/login/LoginUserDataAccessInterface.java
index 681e8a52e..4a13826f4 100644
--- a/src/main/java/use_case/login/LoginUserDataAccessInterface.java
+++ b/src/main/java/osiris/use_case/login/LoginUserDataAccessInterface.java
@@ -1,6 +1,6 @@
-package use_case.login;
+package osiris.use_case.login;
-import entity.User;
+import osiris.entity.User;
/**
* DAO for the Login Use Case.
@@ -22,20 +22,20 @@ public interface LoginUserDataAccessInterface {
/**
* Returns the user with the given username.
- * @param username the username to look up
+ * @param email the username to look up
* @return the user with the given username
*/
- User get(String username);
+ User get(String email);
/**
* Returns the username of the curren user of the application.
* @return the username of the current user; null indicates that no one is logged into the application.
*/
- String getCurrentUsername();
+ String getCurrentEmail();
/**
* Sets the username indicating who is the current user of the application.
- * @param username the new current username; null to indicate that no one is currently logged into the application.
+ * @param email the new current username; null to indicate that no one is currently logged into the application.
*/
- void setCurrentUsername(String username);
+ void setCurrentEmail(String email);
}
diff --git a/src/main/java/use_case/logout/LogoutInputBoundary.java b/src/main/java/osiris/use_case/logout/LogoutInputBoundary.java
similarity index 89%
rename from src/main/java/use_case/logout/LogoutInputBoundary.java
rename to src/main/java/osiris/use_case/logout/LogoutInputBoundary.java
index 189f50168..f2cd813ab 100644
--- a/src/main/java/use_case/logout/LogoutInputBoundary.java
+++ b/src/main/java/osiris/use_case/logout/LogoutInputBoundary.java
@@ -1,4 +1,4 @@
-package use_case.logout;
+package osiris.use_case.logout;
/**
* Input Boundary for actions which are related to logging in.
diff --git a/src/main/java/osiris/use_case/logout/LogoutInputData.java b/src/main/java/osiris/use_case/logout/LogoutInputData.java
new file mode 100644
index 000000000..f996609bc
--- /dev/null
+++ b/src/main/java/osiris/use_case/logout/LogoutInputData.java
@@ -0,0 +1,17 @@
+package osiris.use_case.logout;
+
+/**
+ * The Input Data for the Logout Use Case.
+ */
+public class LogoutInputData {
+ private final String email;
+
+ public LogoutInputData(String email) {
+ this.email = email;
+ }
+
+ public String getEmail() {
+ return email;
+ }
+
+}
diff --git a/src/main/java/osiris/use_case/logout/LogoutInteractor.java b/src/main/java/osiris/use_case/logout/LogoutInteractor.java
new file mode 100644
index 000000000..eb06b5834
--- /dev/null
+++ b/src/main/java/osiris/use_case/logout/LogoutInteractor.java
@@ -0,0 +1,23 @@
+package osiris.use_case.logout;
+
+/**
+ * The Logout Interactor.
+ */
+public class LogoutInteractor implements LogoutInputBoundary {
+ private LogoutUserDataAccessInterface userDataAccessObject;
+ private LogoutOutputBoundary logoutPresenter;
+
+ public LogoutInteractor(LogoutUserDataAccessInterface userDataAccessInterface,
+ LogoutOutputBoundary logoutOutputBoundary) {
+ this.userDataAccessObject = userDataAccessInterface;
+ this.logoutPresenter = logoutOutputBoundary;
+ }
+
+ @Override
+ public void execute(LogoutInputData logoutInputData) {
+ final String email = logoutInputData.getEmail();
+ userDataAccessObject.setCurrentEmail(null);
+ final LogoutOutputData outputData = new LogoutOutputData(email, false);
+ logoutPresenter.prepareSuccessView(outputData);
+ }
+}
diff --git a/src/main/java/use_case/logout/LogoutOutputBoundary.java b/src/main/java/osiris/use_case/logout/LogoutOutputBoundary.java
similarity index 93%
rename from src/main/java/use_case/logout/LogoutOutputBoundary.java
rename to src/main/java/osiris/use_case/logout/LogoutOutputBoundary.java
index 935a06bdc..a5f571720 100644
--- a/src/main/java/use_case/logout/LogoutOutputBoundary.java
+++ b/src/main/java/osiris/use_case/logout/LogoutOutputBoundary.java
@@ -1,4 +1,4 @@
-package use_case.logout;
+package osiris.use_case.logout;
/**
* The output boundary for the Login Use Case.
diff --git a/src/main/java/osiris/use_case/logout/LogoutOutputData.java b/src/main/java/osiris/use_case/logout/LogoutOutputData.java
new file mode 100644
index 000000000..139b9a066
--- /dev/null
+++ b/src/main/java/osiris/use_case/logout/LogoutOutputData.java
@@ -0,0 +1,23 @@
+package osiris.use_case.logout;
+
+/**
+ * Output Data for the Logout Use Case.
+ */
+public class LogoutOutputData {
+
+ private String email;
+ private boolean useCaseFailed;
+
+ public LogoutOutputData(String email, boolean useCaseFailed) {
+ this.email = email;
+ this.useCaseFailed = useCaseFailed;
+ }
+
+ public String getEmail() {
+ return email;
+ }
+
+ public boolean isUseCaseFailed() {
+ return useCaseFailed;
+ }
+}
diff --git a/src/main/java/use_case/logout/LogoutUserDataAccessInterface.java b/src/main/java/osiris/use_case/logout/LogoutUserDataAccessInterface.java
similarity index 77%
rename from src/main/java/use_case/logout/LogoutUserDataAccessInterface.java
rename to src/main/java/osiris/use_case/logout/LogoutUserDataAccessInterface.java
index 8263700e2..d60bd579c 100644
--- a/src/main/java/use_case/logout/LogoutUserDataAccessInterface.java
+++ b/src/main/java/osiris/use_case/logout/LogoutUserDataAccessInterface.java
@@ -1,4 +1,4 @@
-package use_case.logout;
+package osiris.use_case.logout;
/**
* DAO for the Logout Use Case.
@@ -9,11 +9,11 @@ public interface LogoutUserDataAccessInterface {
* Returns the username of the curren user of the application.
* @return the username of the current user
*/
- String getCurrentUsername();
+ String getCurrentEmail();
/**
* Sets the username indicating who is the current user of the application.
* @param username the new current username
*/
- void setCurrentUsername(String username);
+ void setCurrentEmail(String username);
}
diff --git a/src/main/java/osiris/use_case/plaid/CreateLinkTokenInputData.java b/src/main/java/osiris/use_case/plaid/CreateLinkTokenInputData.java
new file mode 100644
index 000000000..9c9642f6b
--- /dev/null
+++ b/src/main/java/osiris/use_case/plaid/CreateLinkTokenInputData.java
@@ -0,0 +1,41 @@
+package osiris.use_case.plaid;
+
+/**
+ * Input data for creating a Plaid Link Token.
+ */
+public class CreateLinkTokenInputData {
+ private final String clientName;
+ private final String[] countryCodes;
+ private final String language;
+ private final String userClientId;
+ private final String[] products;
+
+ public CreateLinkTokenInputData(String clientName, String[] countryCodes, String language,
+ String userClientId, String[] products) {
+ this.clientName = clientName;
+ this.countryCodes = countryCodes;
+ this.language = language;
+ this.userClientId = userClientId;
+ this.products = products;
+ }
+
+ public String getClientName() {
+ return clientName;
+ }
+
+ public String[] getCountryCodes() {
+ return countryCodes;
+ }
+
+ public String getLanguage() {
+ return language;
+ }
+
+ public String getUserClientId() {
+ return userClientId;
+ }
+
+ public String[] getProducts() {
+ return products;
+ }
+}
diff --git a/src/main/java/osiris/use_case/plaid/CreateLinkTokenOutputData.java b/src/main/java/osiris/use_case/plaid/CreateLinkTokenOutputData.java
new file mode 100644
index 000000000..0f96f3ae0
--- /dev/null
+++ b/src/main/java/osiris/use_case/plaid/CreateLinkTokenOutputData.java
@@ -0,0 +1,28 @@
+package osiris.use_case.plaid;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * Output data after creating a Plaid Link Token.
+ */
+public class CreateLinkTokenOutputData {
+
+ @SerializedName("link_token")
+ private final String linkToken;
+
+ @SerializedName("request_id")
+ private final String requestId;
+
+ public CreateLinkTokenOutputData(String linkToken, String requestId) {
+ this.linkToken = linkToken;
+ this.requestId = requestId;
+ }
+
+ public String getLinkToken() {
+ return linkToken;
+ }
+
+ public String getRequestId() {
+ return requestId;
+ }
+}
diff --git a/src/main/java/osiris/use_case/plaid/ExchangePublicTokenInputData.java b/src/main/java/osiris/use_case/plaid/ExchangePublicTokenInputData.java
new file mode 100644
index 000000000..14397d62d
--- /dev/null
+++ b/src/main/java/osiris/use_case/plaid/ExchangePublicTokenInputData.java
@@ -0,0 +1,22 @@
+package osiris.use_case.plaid;
+
+/**
+ * Input data for exchanging a Public Token for an Access Token.
+ */
+public class ExchangePublicTokenInputData {
+ private final String publicToken;
+ private final String userClientId;
+
+ public ExchangePublicTokenInputData(String publicToken, String userClientId) {
+ this.publicToken = publicToken;
+ this.userClientId = userClientId;
+ }
+
+ public String getPublicToken() {
+ return publicToken;
+ }
+
+ public String getUserClientId() {
+ return userClientId;
+ }
+}
diff --git a/src/main/java/osiris/use_case/plaid/ExchangePublicTokenOutputData.java b/src/main/java/osiris/use_case/plaid/ExchangePublicTokenOutputData.java
new file mode 100644
index 000000000..57c33c3a0
--- /dev/null
+++ b/src/main/java/osiris/use_case/plaid/ExchangePublicTokenOutputData.java
@@ -0,0 +1,49 @@
+package osiris.use_case.plaid;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * Output data after exchanging a Public Token for an Access Token.
+ */
+public class ExchangePublicTokenOutputData {
+
+ @SerializedName("access_token")
+ private final String accessToken;
+
+ @SerializedName("item_id")
+ private final String itemId;
+
+ @SerializedName("request_id")
+ private final String requestId;
+
+ public ExchangePublicTokenOutputData(String accessToken, String itemId, String requestId) {
+ this.accessToken = accessToken;
+ this.itemId = itemId;
+ this.requestId = requestId;
+ }
+
+ public String getAccessToken() {
+ return accessToken;
+ }
+
+ public String getItemId() {
+ return itemId;
+ }
+
+ public String getRequestId() {
+ return requestId;
+ }
+
+ @Override
+ public String toString() {
+ return "ExchangePublicTokenOutputData{"
+ +
+ "accessToken='" + accessToken + '\''
+ +
+ ", itemId='" + itemId + '\''
+ +
+ ", requestId='" + requestId + '\''
+ +
+ '}';
+ }
+}
diff --git a/src/main/java/osiris/use_case/plaid/PlaidDataBaseUserAccessObjectInterface.java b/src/main/java/osiris/use_case/plaid/PlaidDataBaseUserAccessObjectInterface.java
new file mode 100644
index 000000000..9b4ce87c6
--- /dev/null
+++ b/src/main/java/osiris/use_case/plaid/PlaidDataBaseUserAccessObjectInterface.java
@@ -0,0 +1,27 @@
+package osiris.use_case.plaid;
+
+import java.sql.SQLException;
+
+import osiris.entity.User;
+
+/**
+ * DAO for the Signup Use Case.
+ */
+public interface PlaidDataBaseUserAccessObjectInterface {
+
+ /**
+ * Sets the access code.
+ * @param user the user to set the access code
+ * @throws SQLException if there is an error setting the access code
+ */
+ void save(User user) throws SQLException;
+
+ /**
+ * Gets the user.
+ * @param username the username of the user to get
+ * @return the user
+ */
+ User get(String username);
+
+ void changePassword(User newUser);
+}
diff --git a/src/main/java/osiris/use_case/plaid/PlaidInputBoundary.java b/src/main/java/osiris/use_case/plaid/PlaidInputBoundary.java
new file mode 100644
index 000000000..39a2cf042
--- /dev/null
+++ b/src/main/java/osiris/use_case/plaid/PlaidInputBoundary.java
@@ -0,0 +1,30 @@
+package osiris.use_case.plaid;
+
+import osiris.utility.exceptions.PlaidUseCaseException;
+
+import java.sql.SQLException;
+
+/**
+ * Interface defining the Plaid Interactor's use cases.
+ */
+public interface PlaidInputBoundary {
+
+ /**
+ * Creates a Plaid Link Token.
+ *
+ * @param inputData The input data required to create a link token.
+ * @return The output data containing the link token.
+ * @throws Exception If an error occurs during the use case execution
+ */
+ CreateLinkTokenOutputData createLinkToken(CreateLinkTokenInputData inputData) throws Exception;
+
+ /**
+ * Exchanges a Public Token for an Access Token.
+ *
+ * @param inputData The input data required to exchange the public token.
+ * @return The output data containing the access token and item ID.
+ * @throws PlaidUseCaseException If an error occurs during the use case execution.
+ */
+ ExchangePublicTokenOutputData exchangePublicToken(
+ ExchangePublicTokenInputData inputData) throws PlaidUseCaseException, SQLException;
+}
diff --git a/src/main/java/osiris/use_case/plaid/PlaidInteractor.java b/src/main/java/osiris/use_case/plaid/PlaidInteractor.java
new file mode 100644
index 000000000..3de57cf68
--- /dev/null
+++ b/src/main/java/osiris/use_case/plaid/PlaidInteractor.java
@@ -0,0 +1,94 @@
+package osiris.use_case.plaid;
+
+import java.io.IOException;
+import java.sql.SQLException;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import osiris.data_access.PlaidDataAccessObject.ExchangeTokenResponse;
+import osiris.data_access.PlaidDataAccessObject.LinkTokenResponse;
+import osiris.entity.User;
+import osiris.entity.UserFactory;
+import osiris.utility.exceptions.PlaidException;
+import osiris.utility.exceptions.PlaidUseCaseException;
+
+/**
+ * The Plaid Interactor implementing the PlaidInputBoundary.
+ */
+@Service
+public class PlaidInteractor implements PlaidInputBoundary {
+
+ private final UserPlaidDataAccessInterface plaidDao;
+ private final PlaidDataBaseUserAccessObjectInterface userDataAccessObject;
+ private final UserFactory userFactory;
+ private String username;
+
+ @Autowired
+ public PlaidInteractor(UserPlaidDataAccessInterface plaidDao,
+ PlaidDataBaseUserAccessObjectInterface userDataAccessObject, UserFactory userFactory) {
+ this.plaidDao = plaidDao;
+ this.userDataAccessObject = userDataAccessObject;
+ this.userFactory = userFactory;
+ }
+
+ /**
+ * Creates a Plaid Link Token.
+ *
+ * @param inputData The input data containing necessary parameters.
+ * @return CreateLinkTokenOutputData containing the link token and request ID.
+ * @throws PlaidUseCaseException If an error occurs while creating the link token.
+ */
+ @Override
+ public CreateLinkTokenOutputData createLinkToken(CreateLinkTokenInputData inputData) throws Exception {
+ try {
+ final LinkTokenResponse response = plaidDao.createLinkToken(
+ inputData.getClientName(),
+ inputData.getCountryCodes(),
+ inputData.getLanguage(),
+ inputData.getUserClientId(),
+ inputData.getProducts()
+ );
+ username = inputData.getUserClientId();
+ return new CreateLinkTokenOutputData(response.link_token, response.request_id);
+ }
+ catch (PlaidException ex) {
+ throw new PlaidUseCaseException("Failed to create Link Token: " + ex.getMessage(), ex);
+ }
+ }
+
+ /**
+ * Exchanges a Public Token for an Access Token.
+ *
+ * @param inputData The input data containing the public token.
+ * @return ExchangePublicTokenOutputData containing the access token, item ID, and request ID.
+ * @throws PlaidUseCaseException If an error occurs while exchanging the public token.
+ */
+ @Override
+ public ExchangePublicTokenOutputData exchangePublicToken(
+ ExchangePublicTokenInputData inputData)
+ throws PlaidUseCaseException, SQLException {
+ try {
+ final ExchangeTokenResponse response = plaidDao.exchangePublicToken(
+ inputData.getPublicToken()
+ );
+ final User user = userDataAccessObject.get(username);
+ final String email = user.getEmail();
+ final String password = user.getPassword();
+ final String accessToken = response.access_token;
+ final User newUser = userFactory.create(email, password, accessToken);
+ userDataAccessObject.save(newUser);
+ return new ExchangePublicTokenOutputData(
+ response.access_token,
+ response.item_id,
+ response.request_id
+ );
+ }
+ catch (PlaidException ex) {
+ throw new PlaidUseCaseException("Failed to exchange Public Token: " + ex.getMessage(), ex);
+ }
+ catch (IOException ex) {
+ throw new PlaidUseCaseException("IO Error while exchanging Public Token.", ex);
+ }
+ }
+}
diff --git a/src/main/java/osiris/use_case/plaid/UserPlaidDataAccessInterface.java b/src/main/java/osiris/use_case/plaid/UserPlaidDataAccessInterface.java
new file mode 100644
index 000000000..56cd562e9
--- /dev/null
+++ b/src/main/java/osiris/use_case/plaid/UserPlaidDataAccessInterface.java
@@ -0,0 +1,34 @@
+package osiris.use_case.plaid;
+
+import java.io.IOException;
+
+import osiris.data_access.PlaidDataAccessObject;
+
+/**
+ * Interface for BankAccount data access operations.
+ */
+public interface UserPlaidDataAccessInterface {
+
+ /**
+ * Creates a link token for the Plaid API.
+ *
+ * @param clientName The name of the client.
+ * @param countryCodes The country codes for the client.
+ * @param language The language for the client.
+ * @param userClientId The user client ID.
+ * @param products The products for the client.
+ * @return A response containing the link token.
+ * @throws Exception If an error occurs while creating the link token.
+ */
+ PlaidDataAccessObject.LinkTokenResponse createLinkToken(String clientName, String[] countryCodes, String language,
+ String userClientId, String[] products) throws Exception;
+
+ /**
+ * Exchanges a public token for an access token.
+ *
+ * @param publicToken The public token to exchange.
+ * @return A response containing the access token.
+ * @throws IOException If an error occurs while exchanging the public token.
+ */
+ PlaidDataAccessObject.ExchangeTokenResponse exchangePublicToken(String publicToken) throws IOException;
+}
diff --git a/src/main/java/use_case/signup/SignupInputBoundary.java b/src/main/java/osiris/use_case/signup/SignupInputBoundary.java
similarity index 56%
rename from src/main/java/use_case/signup/SignupInputBoundary.java
rename to src/main/java/osiris/use_case/signup/SignupInputBoundary.java
index 1cb69e02e..ae51d28b0 100644
--- a/src/main/java/use_case/signup/SignupInputBoundary.java
+++ b/src/main/java/osiris/use_case/signup/SignupInputBoundary.java
@@ -1,4 +1,4 @@
-package use_case.signup;
+package osiris.use_case.signup;
/**
* Input Boundary for actions which are related to signing up.
@@ -12,7 +12,12 @@ public interface SignupInputBoundary {
void execute(SignupInputData signupInputData);
/**
- * Executes the switch to login view use case.
+ * Executes the switch to verify view use case.
*/
- void switchToLoginView();
+ void switchToVerifyView();
+
+ /**
+ * Executes the switch to welcome view use case.
+ */
+ void switchToWelcomeView();
}
diff --git a/src/main/java/use_case/signup/SignupInputData.java b/src/main/java/osiris/use_case/signup/SignupInputData.java
similarity index 50%
rename from src/main/java/use_case/signup/SignupInputData.java
rename to src/main/java/osiris/use_case/signup/SignupInputData.java
index 86c5e8abc..cd5393c91 100644
--- a/src/main/java/use_case/signup/SignupInputData.java
+++ b/src/main/java/osiris/use_case/signup/SignupInputData.java
@@ -1,28 +1,34 @@
-package use_case.signup;
+package osiris.use_case.signup;
/**
* The Input Data for the Signup Use Case.
*/
public class SignupInputData {
- private final String username;
+ private final String email;
private final String password;
private final String repeatPassword;
+ private final String accessCode;
- public SignupInputData(String username, String password, String repeatPassword) {
- this.username = username;
+ public SignupInputData(String email, String password, String repeatPassword, String accessCode) {
+ this.email = email;
this.password = password;
this.repeatPassword = repeatPassword;
+ this.accessCode = accessCode;
}
- String getUsername() {
- return username;
+ String getEmail() {
+ return email;
}
String getPassword() {
return password;
}
+ String getAccessCode() {
+ return accessCode;
+ }
+
public String getRepeatPassword() {
return repeatPassword;
}
diff --git a/src/main/java/osiris/use_case/signup/SignupInteractor.java b/src/main/java/osiris/use_case/signup/SignupInteractor.java
new file mode 100644
index 000000000..3e5f45af1
--- /dev/null
+++ b/src/main/java/osiris/use_case/signup/SignupInteractor.java
@@ -0,0 +1,67 @@
+package osiris.use_case.signup;
+
+import osiris.data_access.EmailServiceImpl;
+import osiris.entity.User;
+import osiris.entity.UserFactory;
+
+import java.security.SecureRandom;
+
+/**
+ * The Signup Interactor.
+ */
+public class SignupInteractor implements SignupInputBoundary {
+ private final SignupUserDataAccessInterface userDataAccessObject;
+ private final SignupOutputBoundary userPresenter;
+ private final UserFactory userFactory;
+ private final EmailServiceImpl emailService;
+
+ public SignupInteractor(SignupUserDataAccessInterface signupDataAccessInterface,
+ SignupOutputBoundary signupOutputBoundary,
+ UserFactory userFactory,
+ EmailServiceImpl emailService) {
+ this.userDataAccessObject = signupDataAccessInterface;
+ this.userPresenter = signupOutputBoundary;
+ this.userFactory = userFactory;
+ this.emailService = emailService;
+ }
+
+ @Override
+ public void execute(SignupInputData signupInputData) {
+ if (userDataAccessObject.existsByName(signupInputData.getEmail())) {
+ userPresenter.prepareFailView("User already exists.");
+ }
+ else if (!signupInputData.getPassword().equals(signupInputData.getRepeatPassword())) {
+ userPresenter.prepareFailView("Passwords don't match.");
+ }
+ else {
+ final User user = userFactory.create(signupInputData.getEmail(), signupInputData.getPassword(),
+ signupInputData.getAccessCode());
+ userDataAccessObject.save(user);
+ final String verificationCode = generateVerificationCode(6);
+ emailService.sendVerificationEmail(user.getEmail(), "Osiris Verification Code", verificationCode);
+ final SignupOutputData signupOutputData = new SignupOutputData(user.getEmail(), false);
+ userPresenter.prepareSuccessView(signupOutputData);
+ }
+ }
+
+ private String generateVerificationCode(int length) {
+ final String characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+ final SecureRandom random = new SecureRandom();
+ final StringBuilder code = new StringBuilder(length);
+ for (int i = 0; i < length; i++) {
+ final int index = random.nextInt(characters.length());
+ code.append(characters.charAt(index));
+ }
+ return code.toString();
+ }
+
+ @Override
+ public void switchToVerifyView() {
+ userPresenter.switchToVerifyView();
+ }
+
+ @Override
+ public void switchToWelcomeView() {
+ userPresenter.switchToWelcomeView();
+ }
+}
diff --git a/src/main/java/use_case/signup/SignupOutputBoundary.java b/src/main/java/osiris/use_case/signup/SignupOutputBoundary.java
similarity index 71%
rename from src/main/java/use_case/signup/SignupOutputBoundary.java
rename to src/main/java/osiris/use_case/signup/SignupOutputBoundary.java
index 314376b93..ce034db24 100644
--- a/src/main/java/use_case/signup/SignupOutputBoundary.java
+++ b/src/main/java/osiris/use_case/signup/SignupOutputBoundary.java
@@ -1,4 +1,4 @@
-package use_case.signup;
+package osiris.use_case.signup;
/**
* The output boundary for the Signup Use Case.
@@ -18,7 +18,12 @@ public interface SignupOutputBoundary {
void prepareFailView(String errorMessage);
/**
- * Switches to the Login View.
+ * Switches to the verify View.
*/
- void switchToLoginView();
+ void switchToVerifyView();
+
+ /**
+ * Switches to the welcome View.
+ */
+ void switchToWelcomeView();
}
diff --git a/src/main/java/use_case/signup/SignupOutputData.java b/src/main/java/osiris/use_case/signup/SignupOutputData.java
similarity index 53%
rename from src/main/java/use_case/signup/SignupOutputData.java
rename to src/main/java/osiris/use_case/signup/SignupOutputData.java
index 6dc74d2fb..3453ca90a 100644
--- a/src/main/java/use_case/signup/SignupOutputData.java
+++ b/src/main/java/osiris/use_case/signup/SignupOutputData.java
@@ -1,21 +1,21 @@
-package use_case.signup;
+package osiris.use_case.signup;
/**
* Output Data for the Signup Use Case.
*/
public class SignupOutputData {
- private final String username;
+ private final String email;
private final boolean useCaseFailed;
- public SignupOutputData(String username, boolean useCaseFailed) {
- this.username = username;
+ public SignupOutputData(String email, boolean useCaseFailed) {
+ this.email = email;
this.useCaseFailed = useCaseFailed;
}
- public String getUsername() {
- return username;
+ public String getEmail() {
+ return email;
}
public boolean isUseCaseFailed() {
diff --git a/src/main/java/use_case/signup/SignupUserDataAccessInterface.java b/src/main/java/osiris/use_case/signup/SignupUserDataAccessInterface.java
similarity index 69%
rename from src/main/java/use_case/signup/SignupUserDataAccessInterface.java
rename to src/main/java/osiris/use_case/signup/SignupUserDataAccessInterface.java
index b9d60f585..3272f090a 100644
--- a/src/main/java/use_case/signup/SignupUserDataAccessInterface.java
+++ b/src/main/java/osiris/use_case/signup/SignupUserDataAccessInterface.java
@@ -1,6 +1,6 @@
-package use_case.signup;
+package osiris.use_case.signup;
-import entity.User;
+import osiris.entity.User;
/**
* DAO for the Signup Use Case.
@@ -9,10 +9,10 @@ public interface SignupUserDataAccessInterface {
/**
* Checks if the given username exists.
- * @param username the username to look for
+ * @param email the username to look for
* @return true if a user with the given username exists; false otherwise
*/
- boolean existsByName(String username);
+ boolean existsByName(String email);
/**
* Saves the user.
diff --git a/src/main/java/osiris/use_case/verify/EmailService.java b/src/main/java/osiris/use_case/verify/EmailService.java
new file mode 100644
index 000000000..63a5f710d
--- /dev/null
+++ b/src/main/java/osiris/use_case/verify/EmailService.java
@@ -0,0 +1,6 @@
+package interface_adapter;
+
+public interface EmailService {
+
+ void sendVerificationEmail(String sendingAddress, String subject, String body);
+}
diff --git a/src/main/java/osiris/use_case/verify/EmailVerificationInteractor.java b/src/main/java/osiris/use_case/verify/EmailVerificationInteractor.java
new file mode 100644
index 000000000..4eb3291c3
--- /dev/null
+++ b/src/main/java/osiris/use_case/verify/EmailVerificationInteractor.java
@@ -0,0 +1,45 @@
+package osiris.use_case.verify;
+
+import java.security.SecureRandom;
+import java.util.HashMap;
+import java.util.Map;
+
+public class EmailVerificationInteractor {
+ private final interface_adapter.EmailService emailService;
+ private final Map verificationCodes = new HashMap<>();
+ private final SecureRandom random = new SecureRandom();
+
+ public EmailVerificationInteractor(interface_adapter.EmailService emailService) {
+ this.emailService = emailService;
+ }
+
+ public void sendVerificationEmail(String email) {
+ String verificationCode = generateVerificationCode(6);
+
+ verificationCodes.put(email, verificationCode);
+
+ // Send the verification email
+ String subject = "Your Verification Code";
+ String message = "Your verification code is: " + verificationCode;
+ emailService.sendVerificationEmail(email, subject, message);
+ }
+
+ public boolean verifyCode(String email, String code) {
+ String storedCode = verificationCodes.get(email);
+ if (storedCode != null && storedCode.equals(code)) {
+ verificationCodes.remove(email);
+ return true;
+ }
+ return false;
+ }
+
+ private String generateVerificationCode(int length) {
+ String characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+ StringBuilder code = new StringBuilder(length);
+ for (int i = 0; i < length; i++) {
+ int index = random.nextInt(characters.length());
+ code.append(characters.charAt(index));
+ }
+ return code.toString();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/osiris/use_case/verify/VerifyInputBoundary.java b/src/main/java/osiris/use_case/verify/VerifyInputBoundary.java
new file mode 100644
index 000000000..bcce22f6c
--- /dev/null
+++ b/src/main/java/osiris/use_case/verify/VerifyInputBoundary.java
@@ -0,0 +1,25 @@
+package osiris.use_case.verify;
+
+/**
+ * The Verify Email Use Case.
+ */
+public interface VerifyInputBoundary {
+ /**
+ * Execute the Verify Email Use Case.
+ * @param verifyInputData the input data for this use case
+ */
+
+ void execute(VerifyInputData verifyInputData);
+
+ /**
+ * Prepares the switch to signup view for the Verify Email Use Case.
+ */
+ void switchToSignUpView();
+
+ /**
+ * Prepares the resending for Verify Email Use Case.
+ * @param verifyInputData the input data for this use case
+ */
+ void resendVerificationEmail(VerifyInputData verifyInputData);
+
+}
diff --git a/src/main/java/osiris/use_case/verify/VerifyInputData.java b/src/main/java/osiris/use_case/verify/VerifyInputData.java
new file mode 100644
index 000000000..fe25cddd4
--- /dev/null
+++ b/src/main/java/osiris/use_case/verify/VerifyInputData.java
@@ -0,0 +1,39 @@
+package osiris.use_case.verify;
+
+/**
+ * The input data for the Verify Email Use Case.
+ */
+
+public class VerifyInputData {
+ private final String email;
+ private final String verifyCode;
+
+ /**
+ * Constructs a new VerifyInputData instance with the specified email and verification code.
+ *
+ * @param email the user's email address
+ * @param verifyCode the verification code
+ */
+ public VerifyInputData(String email, String verifyCode) {
+ this.email = email;
+ this.verifyCode = verifyCode;
+ }
+
+ /**
+ * Returns the user's email address.
+ *
+ * @return the email address
+ */
+ public String getEmail() {
+ return email;
+ }
+
+ /**
+ * Returns the verification code.
+ *
+ * @return the verification code
+ */
+ public String getVerifyCode() {
+ return verifyCode;
+ }
+}
diff --git a/src/main/java/osiris/use_case/verify/VerifyInteractor.java b/src/main/java/osiris/use_case/verify/VerifyInteractor.java
new file mode 100644
index 000000000..445bb7a4f
--- /dev/null
+++ b/src/main/java/osiris/use_case/verify/VerifyInteractor.java
@@ -0,0 +1,62 @@
+package osiris.use_case.verify;
+
+import java.security.SecureRandom;
+
+import osiris.data_access.DBUserDataAccessObject;
+
+/**
+ * The Verify Email Interactor.
+ */
+
+public class VerifyInteractor implements VerifyInputBoundary {
+ private final VerifyOutputBoundary userPresenter;
+ private final interface_adapter.EmailService emailService;
+ private final DBUserDataAccessObject dataAccessObject;
+
+ public VerifyInteractor(VerifyOutputBoundary verifyOutputBoundary, interface_adapter.EmailService emailService,
+ DBUserDataAccessObject dataAccessObject) {
+ this.userPresenter = verifyOutputBoundary;
+ this.emailService = emailService;
+ this.dataAccessObject = dataAccessObject;
+ }
+
+ @Override
+ public void execute(VerifyInputData verifyInputData) {
+ final String email = verifyInputData.getEmail();
+ final String inputCode = verifyInputData.getVerifyCode();
+ final String storedCode = dataAccessObject.getVerificationCode(email);
+ if (storedCode != null && storedCode.equals(inputCode)) {
+ userPresenter.prepareSuccessView(new VerifyOutputData(email, false));
+ }
+ else {
+ userPresenter.prepareFailView("Invalid verification code.");
+ }
+ }
+
+ @Override
+ public void switchToSignUpView() {
+ userPresenter.switchToSignUpView();
+ }
+
+ @Override
+ public void resendVerificationEmail(VerifyInputData inputData) {
+ final String email = inputData.getEmail();
+ final String newVerificationCode = generateVerificationCode(6);
+
+ dataAccessObject.saveVerificationCode(email, newVerificationCode);
+
+ emailService.sendVerificationEmail(email, "Resend Email Verification", "Your new verification code is: "
+ + newVerificationCode);
+ }
+
+ private String generateVerificationCode(int length) {
+ final String characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+ final SecureRandom random = new SecureRandom();
+ final StringBuilder code = new StringBuilder(length);
+ for (int i = 0; i < length; i++) {
+ final int index = random.nextInt(characters.length());
+ code.append(characters.charAt(index));
+ }
+ return code.toString();
+ }
+}
diff --git a/src/main/java/osiris/use_case/verify/VerifyOutputBoundary.java b/src/main/java/osiris/use_case/verify/VerifyOutputBoundary.java
new file mode 100644
index 000000000..eecb33213
--- /dev/null
+++ b/src/main/java/osiris/use_case/verify/VerifyOutputBoundary.java
@@ -0,0 +1,25 @@
+package osiris.use_case.verify;
+
+/**
+ * The output boundary for the Verify Email Use Case.
+ */
+
+public interface VerifyOutputBoundary {
+ /**
+ * Prepares the success view for the Verify Email Use Case.
+ * @param outputData the output data
+ */
+
+ void prepareSuccessView(VerifyOutputData outputData);
+ /**
+ * Prepares the failure view for the Verify Email Use Case.
+ * @param errorMessage the explanation of the failure
+ */
+
+ void prepareFailView(String errorMessage);
+
+ /**
+ * Prepares the switch to signup view for the Verify Email Use Case.
+ */
+ void switchToSignUpView();
+}
diff --git a/src/main/java/osiris/use_case/verify/VerifyOutputData.java b/src/main/java/osiris/use_case/verify/VerifyOutputData.java
new file mode 100644
index 000000000..0c1640172
--- /dev/null
+++ b/src/main/java/osiris/use_case/verify/VerifyOutputData.java
@@ -0,0 +1,15 @@
+package osiris.use_case.verify;
+
+/**
+ * Output Data for the Verify Email Use Case.
+ */
+
+public class VerifyOutputData {
+ private final String username;
+ private final boolean useCaseFailed;
+
+ public VerifyOutputData(String username, boolean useCaseFailed) {
+ this.username = username;
+ this.useCaseFailed = useCaseFailed;
+ }
+}
diff --git a/src/main/java/osiris/use_case/viewexpenses/ViewExpensesInputBoundary.java b/src/main/java/osiris/use_case/viewexpenses/ViewExpensesInputBoundary.java
new file mode 100644
index 000000000..213e253b5
--- /dev/null
+++ b/src/main/java/osiris/use_case/viewexpenses/ViewExpensesInputBoundary.java
@@ -0,0 +1,19 @@
+package osiris.use_case.viewexpenses;
+
+/**
+ * The ImputBoundary for the ViewExpenses.
+ */
+
+public interface ViewExpensesInputBoundary {
+ /**
+ * Processes the input data for viewing expenses.
+ *
+ * @param inputData Input data containing the required information for the use case.
+ */
+ void execute(ViewExpensesInputData inputData);
+
+ /**
+ * Executes the switch to home view use case.
+ */
+ void switchToHomeView();
+}
diff --git a/src/main/java/osiris/use_case/viewexpenses/ViewExpensesInputData.java b/src/main/java/osiris/use_case/viewexpenses/ViewExpensesInputData.java
new file mode 100644
index 000000000..d1e6ef941
--- /dev/null
+++ b/src/main/java/osiris/use_case/viewexpenses/ViewExpensesInputData.java
@@ -0,0 +1,16 @@
+package osiris.use_case.viewexpenses;
+
+import lombok.Getter;
+
+/**
+ * The InputData for the ViewExpenses.
+ */
+@Getter
+public class ViewExpensesInputData {
+ private final String transactionList;
+
+ public ViewExpensesInputData(String transactionList) {
+ this.transactionList = transactionList;
+ }
+
+}
diff --git a/src/main/java/osiris/use_case/viewexpenses/ViewExpensesInteractor.java b/src/main/java/osiris/use_case/viewexpenses/ViewExpensesInteractor.java
new file mode 100644
index 000000000..81db57ef9
--- /dev/null
+++ b/src/main/java/osiris/use_case/viewexpenses/ViewExpensesInteractor.java
@@ -0,0 +1,70 @@
+package osiris.use_case.viewexpenses;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+// import osiris.utility.jfreechart.PieChartUtility;
+
+/**
+ * The GrabTransaction Interactor.
+ */
+public class ViewExpensesInteractor implements ViewExpensesInputBoundary {
+ private final ViewExpensesOutputBoundary presenter;
+
+ public ViewExpensesInteractor(ViewExpensesOutputBoundary presenter) {
+ this.presenter = presenter;
+ }
+
+ @Override
+ public void execute(ViewExpensesInputData inputData) {
+ // Fetch transactions from Plaid API
+ final String transactionsJson = inputData.getTransactionList();
+
+ final Set essentialCategories = new HashSet<>();
+ essentialCategories.add("Groceries");
+ essentialCategories.add("Rent");
+ essentialCategories.add("Utilities");
+ essentialCategories.add("Healthcare");
+ // Initialize totals
+ double essentialTotal = 0.0;
+ double nonEssentialTotal = 0.0;
+
+ // Categorize transactions
+ final JsonObject jsonResponse = JsonParser.parseString(transactionsJson).getAsJsonObject();
+ final JsonArray transactions = jsonResponse.getAsJsonArray("transactions");
+
+ for (JsonElement transactionElement : transactions) {
+ final JsonObject transaction = transactionElement.getAsJsonObject();
+ final double amount = transaction.get("amount").getAsDouble();
+ final JsonArray categories = transaction.getAsJsonArray("category");
+
+ boolean isEssential = false;
+ for (JsonElement categoryElement : categories) {
+ final String category = categoryElement.getAsString();
+ if (essentialCategories.contains(category)) {
+ isEssential = true;
+ break;
+ }
+ }
+ if (isEssential) {
+ essentialTotal += amount;
+ }
+ else {
+ nonEssentialTotal += amount;
+ }
+ }
+ final ViewExpensesOutputData outputData = new ViewExpensesOutputData(essentialTotal, nonEssentialTotal);
+ presenter.prepareChart(outputData);
+ }
+
+ @Override
+ public void switchToHomeView() {
+ presenter.switchToHomeView();
+
+ }
+
+}
diff --git a/src/main/java/osiris/use_case/viewexpenses/ViewExpensesOutputBoundary.java b/src/main/java/osiris/use_case/viewexpenses/ViewExpensesOutputBoundary.java
new file mode 100644
index 000000000..e8d5ad3d7
--- /dev/null
+++ b/src/main/java/osiris/use_case/viewexpenses/ViewExpensesOutputBoundary.java
@@ -0,0 +1,19 @@
+package osiris.use_case.viewexpenses;
+
+/**
+ * ViewExpenses OutBoundary.
+ */
+public interface ViewExpensesOutputBoundary {
+ /**
+ * Handles the output data for viewing expenses.
+ *
+ * @param outputData Output data containing the result of the use case.
+ */
+
+ void prepareChart(ViewExpensesOutputData outputData);
+
+ /**
+ * Executes the switch to home view use case.
+ */
+ void switchToHomeView();
+}
diff --git a/src/main/java/osiris/use_case/viewexpenses/ViewExpensesOutputData.java b/src/main/java/osiris/use_case/viewexpenses/ViewExpensesOutputData.java
new file mode 100644
index 000000000..02f285b03
--- /dev/null
+++ b/src/main/java/osiris/use_case/viewexpenses/ViewExpensesOutputData.java
@@ -0,0 +1,22 @@
+package osiris.use_case.viewexpenses;
+
+/**
+ * ViewExpenses Output Data.
+ */
+public class ViewExpensesOutputData {
+ private final double essentialTotal;
+ private final double nonEssentialTotal;
+
+ public ViewExpensesOutputData(double essentialTotal, double nonEssentialTotal) {
+ this.essentialTotal = essentialTotal;
+ this.nonEssentialTotal = nonEssentialTotal;
+ }
+
+ public double getEssentialTotal() {
+ return essentialTotal;
+ }
+
+ public double getNonEssentialTotal() {
+ return nonEssentialTotal;
+ }
+}
diff --git a/src/main/java/osiris/use_case/welcome/WelcomeInputBoundary.java b/src/main/java/osiris/use_case/welcome/WelcomeInputBoundary.java
new file mode 100644
index 000000000..a573a0c66
--- /dev/null
+++ b/src/main/java/osiris/use_case/welcome/WelcomeInputBoundary.java
@@ -0,0 +1,17 @@
+package osiris.use_case.welcome;
+
+/**
+ * Input boundary for the Welcome Use Case.
+ */
+public interface WelcomeInputBoundary {
+
+ /**
+ * Executes the switch to login view use case.
+ */
+ void switchToLoginView();
+
+ /**
+ * Executes the switch to sign up view use case.
+ */
+ void switchToSignUpView();
+}
diff --git a/src/main/java/osiris/use_case/welcome/WelcomeInteractor.java b/src/main/java/osiris/use_case/welcome/WelcomeInteractor.java
new file mode 100644
index 000000000..33aa5c2e6
--- /dev/null
+++ b/src/main/java/osiris/use_case/welcome/WelcomeInteractor.java
@@ -0,0 +1,24 @@
+package osiris.use_case.welcome;
+
+/**
+ * Interactor for the Welcome Use Case.
+ */
+public class WelcomeInteractor implements WelcomeInputBoundary {
+
+ private final WelcomeOutputBoundary userPresenter;
+
+ public WelcomeInteractor(WelcomeOutputBoundary welcomeOutputBoundary) {
+ this.userPresenter = welcomeOutputBoundary;
+ }
+
+ @Override
+ public void switchToLoginView() {
+ userPresenter.switchToLoginView();
+ }
+
+ @Override
+ public void switchToSignUpView() {
+ userPresenter.switchToSignUpView();
+ }
+
+}
diff --git a/src/main/java/osiris/use_case/welcome/WelcomeOutputBoundary.java b/src/main/java/osiris/use_case/welcome/WelcomeOutputBoundary.java
new file mode 100644
index 000000000..c9a2e4224
--- /dev/null
+++ b/src/main/java/osiris/use_case/welcome/WelcomeOutputBoundary.java
@@ -0,0 +1,17 @@
+package osiris.use_case.welcome;
+
+/**
+ * Output boundary for the Welcome Use Case.
+ */
+public interface WelcomeOutputBoundary {
+
+ /**
+ * Switches to the Login View.
+ */
+ void switchToLoginView();
+
+ /**
+ * Switches to the Sign up View.
+ */
+ void switchToSignUpView();
+}
diff --git a/src/main/java/osiris/utility/data_transfer_objects/ExchangeTokenRequestDataTransferObject.java b/src/main/java/osiris/utility/data_transfer_objects/ExchangeTokenRequestDataTransferObject.java
new file mode 100644
index 000000000..a878c347e
--- /dev/null
+++ b/src/main/java/osiris/utility/data_transfer_objects/ExchangeTokenRequestDataTransferObject.java
@@ -0,0 +1,25 @@
+package osiris.utility.data_transfer_objects;
+
+/**
+ * Data Transfer Object for Exchange Token Requests.
+ */
+public class ExchangeTokenRequestDataTransferObject {
+ private String publicToken;
+ private String userClientId;
+
+ public String getPublicToken() {
+ return publicToken;
+ }
+
+ public void setPublicToken(String publicToken) {
+ this.publicToken = publicToken;
+ }
+
+ public String getUserClientId() {
+ return userClientId;
+ }
+
+ public void setUserClientId(String userClientId) {
+ this.userClientId = userClientId;
+ }
+}
diff --git a/src/main/java/osiris/utility/data_transfer_objects/LinkTokenRequestDataTransferObject.java b/src/main/java/osiris/utility/data_transfer_objects/LinkTokenRequestDataTransferObject.java
new file mode 100644
index 000000000..2deef78a1
--- /dev/null
+++ b/src/main/java/osiris/utility/data_transfer_objects/LinkTokenRequestDataTransferObject.java
@@ -0,0 +1,54 @@
+package osiris.utility.data_transfer_objects;
+
+/**
+ * Data Transfer Object for Link Token Requests.
+ */
+public class LinkTokenRequestDataTransferObject {
+ private String clientName;
+ private String[] countryCodes;
+ private String language;
+ private String userClientId;
+ private String[] products;
+
+ // Getters and setters
+
+ public String getClientName() {
+ return clientName;
+ }
+
+ public void setClientName(String clientName) {
+ this.clientName = clientName;
+ }
+
+ public String[] getCountryCodes() {
+ return countryCodes;
+ }
+
+ public void setCountryCodes(String[] countryCodes) {
+ this.countryCodes = countryCodes;
+ }
+
+ public String getLanguage() {
+ return language;
+ }
+
+ public void setLanguage(String language) {
+ this.language = language;
+ }
+
+ public String getUserClientId() {
+ return userClientId;
+ }
+
+ public void setUserClientId(String userClientId) {
+ this.userClientId = userClientId;
+ }
+
+ public String[] getProducts() {
+ return products;
+ }
+
+ public void setProducts(String[] products) {
+ this.products = products;
+ }
+}
diff --git a/src/main/java/osiris/utility/exceptions/PlaidException.java b/src/main/java/osiris/utility/exceptions/PlaidException.java
new file mode 100644
index 000000000..827d1f133
--- /dev/null
+++ b/src/main/java/osiris/utility/exceptions/PlaidException.java
@@ -0,0 +1,15 @@
+package osiris.utility.exceptions;
+
+/**
+ * Exception thrown when an error occurs during a Plaid operation.
+ */
+public class PlaidException extends RuntimeException {
+
+ public PlaidException(String message) {
+ super(message);
+ }
+
+ public PlaidException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/src/main/java/osiris/utility/exceptions/PlaidUseCaseException.java b/src/main/java/osiris/utility/exceptions/PlaidUseCaseException.java
new file mode 100644
index 000000000..9ff2fb6ec
--- /dev/null
+++ b/src/main/java/osiris/utility/exceptions/PlaidUseCaseException.java
@@ -0,0 +1,14 @@
+package osiris.utility.exceptions;
+
+/**
+ * Exception thrown when an error occurs during a Plaid use case.
+ */
+public class PlaidUseCaseException extends Exception {
+ public PlaidUseCaseException(String message) {
+ super(message);
+ }
+
+ public PlaidUseCaseException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/src/main/java/osiris/utility/jfreechart/PieChartUtility.java b/src/main/java/osiris/utility/jfreechart/PieChartUtility.java
new file mode 100644
index 000000000..a0d090f93
--- /dev/null
+++ b/src/main/java/osiris/utility/jfreechart/PieChartUtility.java
@@ -0,0 +1,43 @@
+package osiris.utility.jfreechart;
+
+import javax.swing.JFrame;
+
+import org.jfree.chart.ChartFactory;
+import org.jfree.chart.ChartPanel;
+import org.jfree.chart.JFreeChart;
+import org.jfree.data.general.DefaultPieDataset;
+
+/**
+ * The Class that helps generate pie chart for ViewExpenses UseCase.
+ */
+public class PieChartUtility {
+
+ /**
+ * The Class that helps displays pie chart for ViewExpenses UseCase.
+ * @param essential a double of essential value
+ * @param nonEssential a double of essential value
+ */
+ public static void displayPieChart(double essential, double nonEssential) {
+ // Create dataset
+ final DefaultPieDataset dataset = new DefaultPieDataset();
+ dataset.setValue("Essential", essential);
+ dataset.setValue("Non-Essential", nonEssential);
+
+ // Create chart
+ final JFreeChart chart = ChartFactory.createPieChart(
+ "Expense Breakdown",
+ dataset,
+ true,
+ true,
+ false
+ );
+
+ // Display chart in JFrame
+ final JFrame frame = new JFrame("Pie Chart");
+ frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
+ frame.add(new ChartPanel(chart));
+ frame.pack();
+ frame.setVisible(true);
+ }
+
+}
diff --git a/src/main/java/osiris/view/BudgetView.java b/src/main/java/osiris/view/BudgetView.java
new file mode 100644
index 000000000..0f5642e53
--- /dev/null
+++ b/src/main/java/osiris/view/BudgetView.java
@@ -0,0 +1,111 @@
+package osiris.view;
+
+import java.awt.Component;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.io.IOException;
+import java.util.ArrayList;
+
+import javax.swing.*;
+
+import osiris.interface_adapter.budget.BudgetController;
+import osiris.interface_adapter.budget.BudgetViewModel;
+import osiris.interface_adapter.grabtransaction.GrabTransactionController;
+import osiris.interface_adapter.budget.BudgetState;
+import osiris.interface_adapter.budget.BudgetViewModel;
+import osiris.interface_adapter.viewexpenses.ViewExpensesController;
+import osiris.use_case.grabtransactions.GrabTransactionOutputData;
+import osiris.utility.exceptions.PlaidUseCaseException;
+
+/**
+ * The View for when the user viewing the budget.
+ */
+public class BudgetView extends JPanel implements PropertyChangeListener {
+ private final String viewName = "budget";
+ private final BudgetViewModel budgetViewModel;
+ private BudgetController controller;
+ private final JButton goBack;
+ private final JButton budgetButton;
+ private GrabTransactionController grabTransactionController;
+
+ public BudgetView(BudgetViewModel budgetViewModel) {
+ this.budgetViewModel = budgetViewModel;
+
+ final JLabel title = new JLabel(BudgetViewModel.TITLE_LABEL);
+ title.setAlignmentX(Component.LEFT_ALIGNMENT);
+
+ final JPanel tablePanel = new JPanel();
+ final JPanel buttons = new JPanel();
+ budgetButton = new JButton(BudgetViewModel.BUTTON_LABEL);
+ goBack = new JButton(BudgetViewModel.BACK_LABEL);
+
+ buttons.add(budgetButton);
+ buttons.add(goBack);
+
+ this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
+
+ budgetButton.addActionListener(
+ // This creates an anonymous subclass of ActionListener and instantiates it.
+ new ActionListener() {
+ public void actionPerformed(ActionEvent evt) {
+ try {
+ final GrabTransactionOutputData transactions = grabTransactionController.createTransactions(
+ "divnoor");
+ controller.execute(String.valueOf(transactions));
+ final BudgetState currentState = budgetViewModel.getState();
+ ArrayList monthlyTotals = currentState.getMonthlyTotals();
+ String[][] tableData = {
+ {"January", String.valueOf(monthlyTotals.get(0))},
+ {"February", String.valueOf(monthlyTotals.get(1))},
+ {"March", String.valueOf(monthlyTotals.get(2))},
+ {"April", String.valueOf(monthlyTotals.get(3))},
+ {"May", String.valueOf(monthlyTotals.get(4))},
+ {"June", String.valueOf(monthlyTotals.get(5))},
+ {"July", String.valueOf(monthlyTotals.get(6))},
+ {"August", String.valueOf(monthlyTotals.get(7))},
+ {"September", String.valueOf(monthlyTotals.get(8))},
+ {"October", String.valueOf(monthlyTotals.get(9))},
+ {"November", String.valueOf(monthlyTotals.get(10))},
+ {"December", String.valueOf(monthlyTotals.get(11))}
+ };
+ String[] columnNames = {"Months", "Total Expenses" };
+ JTable table = new JTable(tableData, columnNames);
+ JScrollPane scrollPane = new JScrollPane(table);
+ tablePanel.add(scrollPane);
+ }
+ catch (PlaidUseCaseException ex) {
+ throw new RuntimeException(ex);
+ }
+ catch (IOException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+ });
+
+ goBack.addActionListener(evt -> controller.switchToHomeView());
+
+ this.add(title);
+ this.add(buttons);
+ this.add(tablePanel);
+
+ }
+
+ public void setBudgetController(BudgetController budgetController) {
+ this.controller = budgetController;
+ }
+
+ public void setGrabTransactionController(GrabTransactionController grabTransactionController) {
+ this.grabTransactionController = grabTransactionController;
+ }
+
+ public String getViewName() {
+ return viewName;
+ }
+
+ @Override
+ public void propertyChange(PropertyChangeEvent evt) {
+ }
+
+}
diff --git a/src/main/java/osiris/view/DashboardView.java b/src/main/java/osiris/view/DashboardView.java
new file mode 100644
index 000000000..aa8081eb0
--- /dev/null
+++ b/src/main/java/osiris/view/DashboardView.java
@@ -0,0 +1,121 @@
+package osiris.view;
+
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.BoxLayout;
+import javax.swing.JButton;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+
+import osiris.interface_adapter.dashboard.DashboardController;
+import osiris.interface_adapter.dashboard.DashboardViewModel;
+import osiris.interface_adapter.welcome.WelcomeViewModel;
+
+
+/**
+ * The View for the Dashboard Use Case.
+ */
+public class DashboardView extends JPanel implements ActionListener {
+
+ public static final String TIMES_NEW_ROMAN = "Times New Roman";
+ public static final int TITTLE_SIZE = 60;
+ public static final int SLOGAN_SIZE = 30;
+ public static final Color COLOR = new Color(239, 221, 206);
+ public static final int BUTTON_FONT_SIZE = 18;
+ public static final Dimension PREFERRED_SIZE_BUTTON = new Dimension(150, 150);
+ private final String viewName = "dashboard";
+
+ private final DashboardViewModel dashboardViewModel;
+ private DashboardController dashboardController;
+
+ private final JButton viewTransactionHistory;
+ private final JButton viewShahCase;
+ private final JButton viewBudget;
+
+ public DashboardView(DashboardViewModel dashboardViewModel) {
+ this.dashboardViewModel = dashboardViewModel;
+
+ final JLabel title = new JLabel(WelcomeViewModel.TITLE_LABEL);
+ title.setFont(new Font(TIMES_NEW_ROMAN, Font.BOLD, TITTLE_SIZE));
+ title.setAlignmentX(Component.CENTER_ALIGNMENT);
+
+ final JLabel slogan = new JLabel(WelcomeViewModel.SLOGAN_LABEL);
+ slogan.setFont(new Font(TIMES_NEW_ROMAN, Font.PLAIN, SLOGAN_SIZE));
+ slogan.setAlignmentX(Component.CENTER_ALIGNMENT);
+
+ final JPanel titles = new JPanel();
+ titles.add(title);
+ titles.add(slogan);
+
+ final JPanel buttons = new JPanel();
+
+ viewTransactionHistory = new JButton(dashboardViewModel.VIEW_TRANSACTION_HISTORY_BUTTON_LABEL);
+ viewTransactionHistory.setForeground(Color.BLACK);
+ viewTransactionHistory.setBackground(COLOR);
+ viewTransactionHistory.setFont(new Font(TIMES_NEW_ROMAN, Font.BOLD, BUTTON_FONT_SIZE));
+ viewTransactionHistory.setAlignmentX(Component.CENTER_ALIGNMENT);
+ viewTransactionHistory.setPreferredSize(PREFERRED_SIZE_BUTTON);
+
+ viewBudget = new JButton(dashboardViewModel.VIEW_BUDGET_BUTTON_LABEL);
+ viewBudget.setForeground(Color.BLACK);
+ viewBudget.setBackground(COLOR);
+ viewBudget.setFont(new Font(TIMES_NEW_ROMAN, Font.BOLD, BUTTON_FONT_SIZE));
+ viewBudget.setAlignmentX(Component.CENTER_ALIGNMENT);
+ viewBudget.setPreferredSize(PREFERRED_SIZE_BUTTON);
+
+ viewShahCase = new JButton(dashboardViewModel.VIEW_SHAHCASE_BUTTON_LABEL);
+ viewShahCase.setForeground(Color.BLACK);
+ viewShahCase.setBackground(COLOR);
+ viewShahCase.setFont(new Font(TIMES_NEW_ROMAN, Font.BOLD, BUTTON_FONT_SIZE));
+ viewShahCase.setAlignmentX(Component.CENTER_ALIGNMENT);
+ viewShahCase.setPreferredSize(PREFERRED_SIZE_BUTTON);
+
+ buttons.add(viewShahCase);
+ buttons.add(viewTransactionHistory);
+ buttons.add(viewBudget);
+
+ viewBudget.addActionListener(
+ new ActionListener() {
+ public void actionPerformed(ActionEvent evt) {
+ dashboardController.switchToBudgetView();
+ }
+ }
+ );
+
+ viewTransactionHistory.addActionListener(
+ new ActionListener() {
+ public void actionPerformed(ActionEvent evt) {
+ dashboardController.switchToViewExpenses();
+ }
+ }
+ );
+
+ viewShahCase.addActionListener(
+ new ActionListener() {
+ public void actionPerformed(ActionEvent evt) {
+ dashboardController.switchToLoginView();
+ }
+ }
+ );
+
+ this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
+ this.add(titles);
+ this.add(buttons);
+ }
+
+ @Override
+ public void actionPerformed(ActionEvent evt) {
+ JOptionPane.showMessageDialog(this, "Cancel not implemented yet.");
+ }
+
+ public String getViewName() {
+ return viewName;
+ }
+
+ public void setDashboardController(DashboardController controller) {
+ this.dashboardController = controller;
+ }
+}
diff --git a/src/main/java/view/LabelTextPanel.java b/src/main/java/osiris/view/LabelTextPanel.java
similarity index 93%
rename from src/main/java/view/LabelTextPanel.java
rename to src/main/java/osiris/view/LabelTextPanel.java
index e7138555f..09192b308 100644
--- a/src/main/java/view/LabelTextPanel.java
+++ b/src/main/java/osiris/view/LabelTextPanel.java
@@ -1,4 +1,4 @@
-package view;
+package osiris.view;
import javax.swing.JLabel;
import javax.swing.JPanel;
diff --git a/src/main/java/view/LoggedInView.java b/src/main/java/osiris/view/LoggedInView.java
similarity index 82%
rename from src/main/java/view/LoggedInView.java
rename to src/main/java/osiris/view/LoggedInView.java
index 76d40c0f1..df59d6435 100644
--- a/src/main/java/view/LoggedInView.java
+++ b/src/main/java/osiris/view/LoggedInView.java
@@ -1,4 +1,4 @@
-package view;
+package osiris.view;
import java.awt.Component;
import java.beans.PropertyChangeEvent;
@@ -13,10 +13,10 @@
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
-import interface_adapter.change_password.ChangePasswordController;
-import interface_adapter.change_password.LoggedInState;
-import interface_adapter.change_password.LoggedInViewModel;
-import interface_adapter.logout.LogoutController;
+import osiris.interface_adapter.change_password.ChangePasswordController;
+import osiris.interface_adapter.change_password.LoggedInState;
+import osiris.interface_adapter.change_password.LoggedInViewModel;
+import osiris.interface_adapter.logout.LogoutController;
/**
* The View for when the user is logged into the program.
@@ -29,7 +29,7 @@ public class LoggedInView extends JPanel implements PropertyChangeListener {
private ChangePasswordController changePasswordController;
private LogoutController logoutController;
- private final JLabel username;
+ private final JLabel email;
private final JButton logOut;
@@ -47,7 +47,7 @@ public LoggedInView(LoggedInViewModel loggedInViewModel) {
new JLabel("Password"), passwordInputField);
final JLabel usernameInfo = new JLabel("Currently logged in: ");
- username = new JLabel();
+ email = new JLabel();
final JPanel buttons = new JPanel();
logOut = new JButton("Log Out");
@@ -89,8 +89,9 @@ public void changedUpdate(DocumentEvent e) {
final LoggedInState currentState = loggedInViewModel.getState();
this.changePasswordController.execute(
- currentState.getUsername(),
- currentState.getPassword()
+ currentState.getEmail(),
+ currentState.getPassword(),
+ currentState.getAccessCode()
);
}
}
@@ -100,16 +101,15 @@ public void changedUpdate(DocumentEvent e) {
// This creates an anonymous subclass of ActionListener and instantiates it.
evt -> {
if (evt.getSource().equals(logOut)) {
- // TODO: execute the logout use case through the Controller
- // 1. get the state out of the loggedInViewModel. It contains the username.
- // 2. Execute the logout Controller.
+ final LoggedInState currentState = loggedInViewModel.getState();
+ this.logoutController.execute(currentState.getEmail());
}
}
);
this.add(title);
this.add(usernameInfo);
- this.add(username);
+ this.add(email);
this.add(passwordInfo);
this.add(passwordErrorField);
@@ -120,11 +120,11 @@ public void changedUpdate(DocumentEvent e) {
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals("state")) {
final LoggedInState state = (LoggedInState) evt.getNewValue();
- username.setText(state.getUsername());
+ email.setText(state.getEmail());
}
else if (evt.getPropertyName().equals("password")) {
final LoggedInState state = (LoggedInState) evt.getNewValue();
- JOptionPane.showMessageDialog(null, "password updated for " + state.getUsername());
+ JOptionPane.showMessageDialog(null, "password updated for " + state.getEmail());
}
}
@@ -138,6 +138,6 @@ public void setChangePasswordController(ChangePasswordController changePasswordC
}
public void setLogoutController(LogoutController logoutController) {
- // TODO: save the logout controller in the instance variable.
+ this.logoutController = logoutController;
}
}
diff --git a/src/main/java/view/LoginView.java b/src/main/java/osiris/view/LoginView.java
similarity index 74%
rename from src/main/java/view/LoginView.java
rename to src/main/java/osiris/view/LoginView.java
index 96d4f3845..7ac0c0dfb 100644
--- a/src/main/java/view/LoginView.java
+++ b/src/main/java/osiris/view/LoginView.java
@@ -1,10 +1,13 @@
-package view;
+package osiris.view;
-import java.awt.Component;
+import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
+import java.net.URI;
+import java.net.URLEncoder;
+import java.util.Objects;
import javax.swing.BoxLayout;
import javax.swing.JButton;
@@ -15,9 +18,10 @@
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
-import interface_adapter.login.LoginController;
-import interface_adapter.login.LoginState;
-import interface_adapter.login.LoginViewModel;
+import osiris.interface_adapter.login.LoginController;
+import osiris.interface_adapter.login.LoginState;
+import osiris.interface_adapter.login.LoginViewModel;
+
/**
* The View for when the user is logging into the program.
@@ -27,14 +31,14 @@ public class LoginView extends JPanel implements ActionListener, PropertyChangeL
private final String viewName = "log in";
private final LoginViewModel loginViewModel;
- private final JTextField usernameInputField = new JTextField(15);
- private final JLabel usernameErrorField = new JLabel();
+ private final JTextField emailInputField = new JTextField(15);
+ private final JLabel emailErrorField = new JLabel();
private final JPasswordField passwordInputField = new JPasswordField(15);
private final JLabel passwordErrorField = new JLabel();
private final JButton logIn;
- private final JButton cancel;
+ private final JButton goBack;
private LoginController loginController;
public LoginView(LoginViewModel loginViewModel) {
@@ -45,16 +49,16 @@ public LoginView(LoginViewModel loginViewModel) {
final JLabel title = new JLabel("Login Screen");
title.setAlignmentX(Component.CENTER_ALIGNMENT);
- final LabelTextPanel usernameInfo = new LabelTextPanel(
- new JLabel("Username"), usernameInputField);
+ final LabelTextPanel emailInfo = new LabelTextPanel(
+ new JLabel("Email"), emailInputField);
final LabelTextPanel passwordInfo = new LabelTextPanel(
new JLabel("Password"), passwordInputField);
final JPanel buttons = new JPanel();
logIn = new JButton("log in");
buttons.add(logIn);
- cancel = new JButton("cancel");
- buttons.add(cancel);
+ goBack = new JButton("Go back");
+ buttons.add(goBack);
logIn.addActionListener(
new ActionListener() {
@@ -63,21 +67,28 @@ public void actionPerformed(ActionEvent evt) {
final LoginState currentState = loginViewModel.getState();
loginController.execute(
- currentState.getUsername(),
- currentState.getPassword()
+ currentState.getEmail(),
+ currentState.getPassword(),
+ currentState.getAccessCode()
);
}
}
}
);
- cancel.addActionListener(this);
+ goBack.addActionListener(
+ new ActionListener() {
+ public void actionPerformed(ActionEvent evt) {
+ loginController.switchToWelcomeView();
+ }
+ }
+ );
- usernameInputField.getDocument().addDocumentListener(new DocumentListener() {
+ emailInputField.getDocument().addDocumentListener(new DocumentListener() {
private void documentListenerHelper() {
final LoginState currentState = loginViewModel.getState();
- currentState.setUsername(usernameInputField.getText());
+ currentState.setEmail(emailInputField.getText());
loginViewModel.setState(currentState);
}
@@ -124,8 +135,8 @@ public void changedUpdate(DocumentEvent e) {
});
this.add(title);
- this.add(usernameInfo);
- this.add(usernameErrorField);
+ this.add(emailInfo);
+ this.add(emailErrorField);
this.add(passwordInfo);
this.add(buttons);
}
@@ -142,11 +153,11 @@ public void actionPerformed(ActionEvent evt) {
public void propertyChange(PropertyChangeEvent evt) {
final LoginState state = (LoginState) evt.getNewValue();
setFields(state);
- usernameErrorField.setText(state.getLoginError());
+ emailErrorField.setText(state.getLoginError());
}
private void setFields(LoginState state) {
- usernameInputField.setText(state.getUsername());
+ emailInputField.setText(state.getEmail());
passwordInputField.setText(state.getPassword());
}
diff --git a/src/main/java/view/SignupView.java b/src/main/java/osiris/view/SignupView.java
similarity index 76%
rename from src/main/java/view/SignupView.java
rename to src/main/java/osiris/view/SignupView.java
index f98570d62..c8bf6f292 100644
--- a/src/main/java/view/SignupView.java
+++ b/src/main/java/osiris/view/SignupView.java
@@ -1,4 +1,4 @@
-package view;
+package osiris.view;
import java.awt.Component;
import java.awt.event.ActionEvent;
@@ -16,9 +16,9 @@
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
-import interface_adapter.signup.SignupController;
-import interface_adapter.signup.SignupState;
-import interface_adapter.signup.SignupViewModel;
+import osiris.interface_adapter.signup.SignupController;
+import osiris.interface_adapter.signup.SignupState;
+import osiris.interface_adapter.signup.SignupViewModel;
/**
* The View for the Signup Use Case.
@@ -27,14 +27,13 @@ public class SignupView extends JPanel implements ActionListener, PropertyChange
private final String viewName = "sign up";
private final SignupViewModel signupViewModel;
- private final JTextField usernameInputField = new JTextField(15);
+ private final JTextField emailInputField = new JTextField(15);
private final JPasswordField passwordInputField = new JPasswordField(15);
private final JPasswordField repeatPasswordInputField = new JPasswordField(15);
private SignupController signupController;
private final JButton signUp;
- private final JButton cancel;
- private final JButton toLogin;
+ private final JButton goBack;
public SignupView(SignupViewModel signupViewModel) {
this.signupViewModel = signupViewModel;
@@ -43,20 +42,18 @@ public SignupView(SignupViewModel signupViewModel) {
final JLabel title = new JLabel(SignupViewModel.TITLE_LABEL);
title.setAlignmentX(Component.CENTER_ALIGNMENT);
- final LabelTextPanel usernameInfo = new LabelTextPanel(
- new JLabel(SignupViewModel.USERNAME_LABEL), usernameInputField);
+ final LabelTextPanel emailInfo = new LabelTextPanel(
+ new JLabel(SignupViewModel.EMAIL_LABEL), emailInputField);
final LabelTextPanel passwordInfo = new LabelTextPanel(
new JLabel(SignupViewModel.PASSWORD_LABEL), passwordInputField);
final LabelTextPanel repeatPasswordInfo = new LabelTextPanel(
new JLabel(SignupViewModel.REPEAT_PASSWORD_LABEL), repeatPasswordInputField);
final JPanel buttons = new JPanel();
- toLogin = new JButton(SignupViewModel.TO_LOGIN_BUTTON_LABEL);
- buttons.add(toLogin);
signUp = new JButton(SignupViewModel.SIGNUP_BUTTON_LABEL);
buttons.add(signUp);
- cancel = new JButton(SignupViewModel.CANCEL_BUTTON_LABEL);
- buttons.add(cancel);
+ goBack = new JButton(SignupViewModel.RETURN_BUTTON_LABEL);
+ buttons.add(goBack);
signUp.addActionListener(
// This creates an anonymous subclass of ActionListener and instantiates it.
@@ -66,44 +63,43 @@ public void actionPerformed(ActionEvent evt) {
final SignupState currentState = signupViewModel.getState();
signupController.execute(
- currentState.getUsername(),
+ currentState.getEmail(),
currentState.getPassword(),
- currentState.getRepeatPassword()
+ currentState.getRepeatPassword(),
+ currentState.getAccessCode()
);
}
}
}
);
- toLogin.addActionListener(
+ goBack.addActionListener(
new ActionListener() {
public void actionPerformed(ActionEvent evt) {
- signupController.switchToLoginView();
+ signupController.switchToWelcomeView();
}
}
);
- cancel.addActionListener(this);
-
- addUsernameListener();
+ addEmailListener();
addPasswordListener();
addRepeatPasswordListener();
this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
this.add(title);
- this.add(usernameInfo);
+ this.add(emailInfo);
this.add(passwordInfo);
this.add(repeatPasswordInfo);
this.add(buttons);
}
- private void addUsernameListener() {
- usernameInputField.getDocument().addDocumentListener(new DocumentListener() {
+ private void addEmailListener() {
+ emailInputField.getDocument().addDocumentListener(new DocumentListener() {
private void documentListenerHelper() {
final SignupState currentState = signupViewModel.getState();
- currentState.setUsername(usernameInputField.getText());
+ currentState.setEmail(emailInputField.getText());
signupViewModel.setState(currentState);
}
@@ -184,8 +180,8 @@ public void actionPerformed(ActionEvent evt) {
@Override
public void propertyChange(PropertyChangeEvent evt) {
final SignupState state = (SignupState) evt.getNewValue();
- if (state.getUsernameError() != null) {
- JOptionPane.showMessageDialog(this, state.getUsernameError());
+ if (state.getEmailError() != null) {
+ JOptionPane.showMessageDialog(this, state.getEmailError());
}
}
@@ -193,7 +189,26 @@ public String getViewName() {
return viewName;
}
+ private void handleSignup() {
+ String email = emailInputField.getText();
+ String password = new String(passwordInputField.getPassword());
+ String repeatPassword = new String(repeatPasswordInputField.getPassword());
+
+ if (email.isEmpty() || password.isEmpty() || repeatPassword.isEmpty()) {
+ JOptionPane.showMessageDialog(this, "All fields are required.");
+ return;
+ }
+
+ if (!password.equals(repeatPassword)) {
+ JOptionPane.showMessageDialog(this, "Passwords do not match.");
+ return;
+ }
+
+ signupController.execute(email, password, repeatPassword, "");
+ }
+
public void setSignupController(SignupController controller) {
this.signupController = controller;
}
+
}
diff --git a/src/main/java/osiris/view/VerifyView.java b/src/main/java/osiris/view/VerifyView.java
new file mode 100644
index 000000000..3231055b6
--- /dev/null
+++ b/src/main/java/osiris/view/VerifyView.java
@@ -0,0 +1,111 @@
+package osiris.view;
+
+import java.awt.Component;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.swing.BoxLayout;
+import javax.swing.JButton;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+
+import osiris.interface_adapter.verify.VerifyController;
+import osiris.interface_adapter.verify.VerifyState;
+import osiris.interface_adapter.verify.VerifyViewModel;
+
+/**
+ * The View for when the user is logged into the program.
+ */
+
+public class VerifyView extends JPanel implements PropertyChangeListener {
+
+ private final String viewName = "verify";
+ private final VerifyViewModel verifyViewModel;
+ private final JTextField verifyCodeField = new JTextField(15);
+ private VerifyController verifyController;
+ private final JButton goBack;
+ private final JButton verifyButton;
+ private final JButton resendButton;
+
+ public VerifyView(VerifyViewModel verifyViewModel) {
+ this.verifyViewModel = verifyViewModel;
+ this.verifyViewModel.addPropertyChangeListener(this);
+
+ final JLabel title = new JLabel(VerifyViewModel.TITLE_LABEL);
+ title.setAlignmentX(Component.LEFT_ALIGNMENT);
+
+ final LabelTextPanel verifyCodeInfo = new LabelTextPanel(
+ new JLabel(verifyViewModel.VERIFY_LABEL), verifyCodeField);
+
+ final JPanel buttons = new JPanel();
+ verifyButton = new JButton(VerifyViewModel.VERIFY_BUTTON_LABEL);
+ goBack = new JButton(VerifyViewModel.RETURN_BUTTON_LABEL);
+ resendButton = new JButton("Resend Verification Email");
+
+ buttons.add(verifyButton);
+ buttons.add(resendButton);
+ buttons.add(goBack);
+
+ this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
+
+ verifyButton.addActionListener(evt -> {
+ if (evt.getSource().equals(verifyButton)) {
+ final VerifyState currentState = verifyViewModel.getState();
+ final String username = currentState.getUsername();
+ final String verifyCode = verifyCodeField.getText().trim();
+
+ if (username == null || username.isEmpty()) {
+ JOptionPane.showMessageDialog(this, "Username is not available. Please go back and sign up again.");
+ return;
+ }
+
+ if (verifyCode.isEmpty()) {
+ JOptionPane.showMessageDialog(this, "Please enter the verification code.");
+ return;
+ }
+
+ verifyController.execute(username, verifyCode);
+ }
+ });
+
+ goBack.addActionListener(evt -> verifyController.switchToSignUpView());
+
+ resendButton.addActionListener(evt -> handleResendVerificationEmail());
+
+ this.add(title);
+ this.add(verifyCodeInfo);
+ this.add(buttons);
+ }
+
+ private void handleResendVerificationEmail() {
+ final VerifyState currentState = verifyViewModel.getState();
+ final String username = currentState.getUsername();
+ if (username == null || username.isEmpty()) {
+ JOptionPane.showMessageDialog(this, "Username is not available. Please go back and sign up again.");
+ return;
+ }
+ verifyController.resendVerificationEmail(username);
+ }
+
+ @Override
+ public void propertyChange(PropertyChangeEvent evt) {
+ if (evt.getPropertyName().equals("state")) {
+ final VerifyState state = (VerifyState) evt.getNewValue();
+ }
+ else if (evt.getPropertyName().equals("password")) {
+ final VerifyState state = (VerifyState) evt.getNewValue();
+ JOptionPane.showMessageDialog(null, "Password updated for " + state.getUsername());
+ }
+ }
+
+ public String getViewName() {
+ return viewName;
+ }
+
+ public void setVerifyController(VerifyController verifyController) {
+ this.verifyController = verifyController;
+ }
+}
+
diff --git a/src/main/java/osiris/view/ViewExpenses.java b/src/main/java/osiris/view/ViewExpenses.java
new file mode 100644
index 000000000..ba7765784
--- /dev/null
+++ b/src/main/java/osiris/view/ViewExpenses.java
@@ -0,0 +1,100 @@
+package osiris.view;
+
+import java.awt.Component;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.io.IOException;
+
+import javax.swing.BoxLayout;
+import javax.swing.JButton;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+
+import lombok.Getter;
+import osiris.interface_adapter.grabtransaction.GrabTransactionController;
+import osiris.interface_adapter.viewexpenses.ViewExpensesController;
+import osiris.interface_adapter.viewexpenses.ViewExpensesState;
+import osiris.interface_adapter.viewexpenses.ViewExpensesViewModel;
+import osiris.use_case.grabtransactions.GrabTransactionOutputData;
+import osiris.utility.exceptions.PlaidUseCaseException;
+import osiris.utility.jfreechart.PieChartUtility;
+// import osiris.use_case.viewexpenses.ViewExpensesInteractor;
+
+/**
+ * The View for when the user is logged into the program.
+ */
+
+public class ViewExpenses extends JPanel implements PropertyChangeListener {
+
+ @Getter
+ private final String viewName = "view expenses";
+ private final ViewExpensesViewModel viewExpensesViewModel;
+ private ViewExpensesController controller;
+ private final JButton goBack;
+ private final JButton expensesButton;
+ private GrabTransactionController grabTransactionController;
+
+ public ViewExpenses(ViewExpensesViewModel viewExpensesViewModel) {
+ this.viewExpensesViewModel = viewExpensesViewModel;
+
+ final JLabel title = new JLabel(ViewExpensesViewModel.TITLE_LABEL);
+ title.setAlignmentX(Component.LEFT_ALIGNMENT);
+
+ final JPanel buttons = new JPanel();
+ expensesButton = new JButton(ViewExpensesViewModel.BUTTON_LABEL);
+ goBack = new JButton(ViewExpensesViewModel.BACK_LABEL);
+
+ buttons.add(expensesButton);
+ buttons.add(goBack);
+
+ this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
+
+ expensesButton.addActionListener(
+ // This creates an anonymous subclass of ActionListener and instantiates it.
+ new ActionListener() {
+ public void actionPerformed(ActionEvent evt) {
+ try {
+ final GrabTransactionOutputData transactions = grabTransactionController.createTransactions(
+ "divnoor");
+ controller.execute(String.valueOf(transactions));
+ final ViewExpensesState currentState = viewExpensesViewModel.getState();
+ PieChartUtility.displayPieChart(currentState.getEssential(), currentState.getNonEssential()
+ );
+ }
+ catch (PlaidUseCaseException ex) {
+ throw new RuntimeException(ex);
+ }
+ catch (IOException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+ });
+
+ // expensesButton.addActionListener(
+ // new ActionListener() {
+ // public void actionPerformed(ActionEvent evt) {
+ // PieChartUtility.displayPieChart(200.9, 300.45);
+ // }
+ // });
+
+ goBack.addActionListener(evt -> controller.switchToHomeView());
+
+ this.add(title);
+ this.add(buttons);
+ }
+
+ public void setViewExpensesController(ViewExpensesController viewExpensesController) {
+ this.controller = viewExpensesController;
+ }
+
+ public void setGrabTransactionController(GrabTransactionController grabTransactionController) {
+ this.grabTransactionController = grabTransactionController;
+ }
+
+ @Override
+ public void propertyChange(PropertyChangeEvent evt) {
+ }
+}
+
diff --git a/src/main/java/view/ViewManager.java b/src/main/java/osiris/view/ViewManager.java
similarity index 93%
rename from src/main/java/view/ViewManager.java
rename to src/main/java/osiris/view/ViewManager.java
index 7f9f07ec6..073b11758 100644
--- a/src/main/java/view/ViewManager.java
+++ b/src/main/java/osiris/view/ViewManager.java
@@ -1,4 +1,4 @@
-package view;
+package osiris.view;
import java.awt.CardLayout;
import java.beans.PropertyChangeEvent;
@@ -6,7 +6,7 @@
import javax.swing.JPanel;
-import interface_adapter.ViewManagerModel;
+import osiris.interface_adapter.ViewManagerModel;
/**
* The View Manager for the program. It listens for property change events
diff --git a/src/main/java/osiris/view/WelcomeView.java b/src/main/java/osiris/view/WelcomeView.java
new file mode 100644
index 000000000..50e8cfe33
--- /dev/null
+++ b/src/main/java/osiris/view/WelcomeView.java
@@ -0,0 +1,130 @@
+package osiris.view;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.BoxLayout;
+import javax.swing.JButton;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+
+import org.jetbrains.annotations.NotNull;
+
+import osiris.interface_adapter.welcome.WelcomeController;
+import osiris.interface_adapter.welcome.WelcomeViewModel;
+
+/**
+ * The View for the Signup Use Case.
+ */
+public class WelcomeView extends JPanel implements ActionListener {
+ public static final String TIMES_NEW_ROMAN = "Times New Roman";
+ public static final int TITTLE_SIZE = 60;
+ public static final int SLOGAN_SIZE = 30;
+ public static final Color COLOR = new Color(239, 221, 206);
+ public static final int LOGIN_BUTTON_FONT_SIZE = 20;
+ public static final Dimension PREFERRED_SIZE_LOGIN_BUTTON = new Dimension(200, 40);
+ private final String viewName = "welcome";
+
+ private final WelcomeViewModel welcomeViewModel;
+ private WelcomeController welcomeController;
+
+ private final JButton toLogin;
+ private final JButton createAccount;
+ private final JButton emptyButton;
+
+ public WelcomeView(WelcomeViewModel welcomeViewModel) {
+ this.welcomeViewModel = welcomeViewModel;
+
+ final JLabel title = getTitleLabel(WelcomeViewModel.TITLE_LABEL, Font.BOLD, TITTLE_SIZE);
+
+ final JLabel slogan = getTitleLabel(WelcomeViewModel.SLOGAN_LABEL, Font.PLAIN, SLOGAN_SIZE);
+
+ final JPanel titles = new JPanel();
+ titles.add(title);
+ titles.add(slogan);
+
+ final JPanel buttons = new JPanel();
+
+ toLogin = new JButton(welcomeViewModel.LOGIN_BUTTON_LABEL);
+ toLogin.setForeground(Color.BLACK);
+ toLogin.setBackground(COLOR);
+ toLogin.setFont(new Font(TIMES_NEW_ROMAN, Font.BOLD, LOGIN_BUTTON_FONT_SIZE));
+ toLogin.setAlignmentX(Component.CENTER_ALIGNMENT);
+ toLogin.setPreferredSize(PREFERRED_SIZE_LOGIN_BUTTON);
+
+ emptyButton = new JButton(" ");
+ emptyButton.setOpaque(false);
+ emptyButton.setContentAreaFilled(false);
+ emptyButton.setBorderPainted(false);
+ emptyButton.setFocusPainted(false);
+
+ createAccount = new JButton(welcomeViewModel.CREATE_ACCOUNT_BUTTON_LABEL);
+ createAccount.setForeground(Color.BLACK);
+ createAccount.setBackground(COLOR);
+ createAccount.setFont(new Font(TIMES_NEW_ROMAN, Font.BOLD, LOGIN_BUTTON_FONT_SIZE));
+ createAccount.setAlignmentX(Component.CENTER_ALIGNMENT);
+ createAccount.setPreferredSize(PREFERRED_SIZE_LOGIN_BUTTON);
+
+ addButtons(buttons);
+
+ addLoginButton();
+
+ addCreateAccountButton();
+
+ this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
+ this.add(titles);
+ this.add(buttons);
+ }
+
+ private void addButtons(JPanel buttons) {
+ buttons.add(emptyButton);
+ buttons.add(toLogin);
+ buttons.add(createAccount);
+ }
+
+ @NotNull
+ private JLabel getTitleLabel(String titleLabel, int bold, int tittleSize) {
+ final JLabel title = new JLabel(titleLabel);
+ title.setFont(new Font(TIMES_NEW_ROMAN, bold, tittleSize));
+ title.setAlignmentX(Component.CENTER_ALIGNMENT);
+ return title;
+ }
+
+ private void addCreateAccountButton() {
+ createAccount.addActionListener(
+ new ActionListener() {
+ public void actionPerformed(ActionEvent evt) {
+ welcomeController.switchToSignUpView();
+ }
+ }
+ );
+ }
+
+ private void addLoginButton() {
+ toLogin.addActionListener(
+ new ActionListener() {
+ public void actionPerformed(ActionEvent evt) {
+ welcomeController.switchToLoginView();
+ }
+ }
+ );
+ }
+
+ @Override
+ public void actionPerformed(ActionEvent evt) {
+ JOptionPane.showMessageDialog(this, "Cancel not implemented yet.");
+ }
+
+ public String getViewName() {
+ return viewName;
+ }
+
+ public void setWelcomeController(WelcomeController controller) {
+ this.welcomeController = controller;
+ }
+}
diff --git a/src/main/java/use_case/change_password/ChangePasswordInputData.java b/src/main/java/use_case/change_password/ChangePasswordInputData.java
deleted file mode 100644
index 8e09d8d12..000000000
--- a/src/main/java/use_case/change_password/ChangePasswordInputData.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package use_case.change_password;
-
-/**
- * The input data for the Change Password Use Case.
- */
-public class ChangePasswordInputData {
-
- private final String password;
- private final String username;
-
- public ChangePasswordInputData(String password, String username) {
- this.password = password;
- this.username = username;
- }
-
- String getPassword() {
- return password;
- }
-
- String getUsername() {
- return username;
- }
-
-}
diff --git a/src/main/java/use_case/login/LoginInputData.java b/src/main/java/use_case/login/LoginInputData.java
deleted file mode 100644
index 363316832..000000000
--- a/src/main/java/use_case/login/LoginInputData.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package use_case.login;
-
-/**
- * The Input Data for the Login Use Case.
- */
-public class LoginInputData {
-
- private final String username;
- private final String password;
-
- public LoginInputData(String username, String password) {
- this.username = username;
- this.password = password;
- }
-
- String getUsername() {
- return username;
- }
-
- String getPassword() {
- return password;
- }
-
-}
diff --git a/src/main/java/use_case/login/LoginOutputData.java b/src/main/java/use_case/login/LoginOutputData.java
deleted file mode 100644
index 3ea119a8f..000000000
--- a/src/main/java/use_case/login/LoginOutputData.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package use_case.login;
-
-/**
- * Output Data for the Login Use Case.
- */
-public class LoginOutputData {
-
- private final String username;
- private final boolean useCaseFailed;
-
- public LoginOutputData(String username, boolean useCaseFailed) {
- this.username = username;
- this.useCaseFailed = useCaseFailed;
- }
-
- public String getUsername() {
- return username;
- }
-
-}
diff --git a/src/main/java/use_case/logout/LogoutInputData.java b/src/main/java/use_case/logout/LogoutInputData.java
deleted file mode 100644
index 56a33b375..000000000
--- a/src/main/java/use_case/logout/LogoutInputData.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package use_case.logout;
-
-/**
- * The Input Data for the Logout Use Case.
- */
-public class LogoutInputData {
-
- public LogoutInputData(String username) {
- // TODO: save the current username in an instance variable and add a getter.
- }
-
-}
diff --git a/src/main/java/use_case/logout/LogoutInteractor.java b/src/main/java/use_case/logout/LogoutInteractor.java
deleted file mode 100644
index 1ca93b44e..000000000
--- a/src/main/java/use_case/logout/LogoutInteractor.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package use_case.logout;
-
-/**
- * The Logout Interactor.
- */
-public class LogoutInteractor implements LogoutInputBoundary {
- private LogoutUserDataAccessInterface userDataAccessObject;
- private LogoutOutputBoundary logoutPresenter;
-
- public LogoutInteractor(LogoutUserDataAccessInterface userDataAccessInterface,
- LogoutOutputBoundary logoutOutputBoundary) {
- // TODO: save the DAO and Presenter in the instance variables.
- // Which parameter is the DAO and which is the presenter?
- }
-
- @Override
- public void execute(LogoutInputData logoutInputData) {
- // TODO: implement the logic of the Logout Use Case (depends on the LogoutInputData.java TODO)
- // * get the username out of the input data,
- // * set the username to null in the DAO
- // * instantiate the `LogoutOutputData`, which needs to contain the username.
- // * tell the presenter to prepare a success view.
- }
-}
-
diff --git a/src/main/java/use_case/logout/LogoutOutputData.java b/src/main/java/use_case/logout/LogoutOutputData.java
deleted file mode 100644
index 974279155..000000000
--- a/src/main/java/use_case/logout/LogoutOutputData.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package use_case.logout;
-
-/**
- * Output Data for the Logout Use Case.
- */
-public class LogoutOutputData {
-
- private String username;
- private boolean useCaseFailed;
-
- public LogoutOutputData(String username, boolean useCaseFailed) {
- // TODO: save the parameters in the instance variables.
- }
-
- public String getUsername() {
- return username;
- }
-
- public boolean isUseCaseFailed() {
- return useCaseFailed;
- }
-}
diff --git a/src/main/java/use_case/signup/SignupInteractor.java b/src/main/java/use_case/signup/SignupInteractor.java
deleted file mode 100644
index 3fd6560c7..000000000
--- a/src/main/java/use_case/signup/SignupInteractor.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package use_case.signup;
-
-import entity.User;
-import entity.UserFactory;
-
-/**
- * The Signup Interactor.
- */
-public class SignupInteractor implements SignupInputBoundary {
- private final SignupUserDataAccessInterface userDataAccessObject;
- private final SignupOutputBoundary userPresenter;
- private final UserFactory userFactory;
-
- public SignupInteractor(SignupUserDataAccessInterface signupDataAccessInterface,
- SignupOutputBoundary signupOutputBoundary,
- UserFactory userFactory) {
- this.userDataAccessObject = signupDataAccessInterface;
- this.userPresenter = signupOutputBoundary;
- this.userFactory = userFactory;
- }
-
- @Override
- public void execute(SignupInputData signupInputData) {
- if (userDataAccessObject.existsByName(signupInputData.getUsername())) {
- userPresenter.prepareFailView("User already exists.");
- }
- else if (!signupInputData.getPassword().equals(signupInputData.getRepeatPassword())) {
- userPresenter.prepareFailView("Passwords don't match.");
- }
- else {
- final User user = userFactory.create(signupInputData.getUsername(), signupInputData.getPassword());
- userDataAccessObject.save(user);
-
- final SignupOutputData signupOutputData = new SignupOutputData(user.getName(), false);
- userPresenter.prepareSuccessView(signupOutputData);
- }
- }
-
- @Override
- public void switchToLoginView() {
- userPresenter.switchToLoginView();
- }
-}
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
new file mode 100644
index 000000000..397c54997
--- /dev/null
+++ b/src/main/resources/application.properties
@@ -0,0 +1,5 @@
+plaid.client_id=67391a194ef4a3001a48f150
+plaid.secret=fdb61466892e58bd9e82685d2d5673
+plaid.environment=sandbox
+
+
diff --git a/src/main/resources/static/app.js b/src/main/resources/static/app.js
new file mode 100644
index 000000000..f2d99adf3
--- /dev/null
+++ b/src/main/resources/static/app.js
@@ -0,0 +1,123 @@
+/**
+ * Parses a query parameter from the URL.
+ * @param {string} name - The name of the parameter to retrieve.
+ * @returns {string|null} The value of the parameter or null if not found.
+ */
+function getQueryParam(name) {
+ const urlParams = new URLSearchParams(window.location.search);
+ return urlParams.get(name);
+}
+
+
+
+const email = getQueryParam('email');
+
+if (!email) {
+ alert('Email is missing from URL parameters.');
+ // Handle the missing email appropriately
+}
+
+document.getElementById('link-button').addEventListener('click', function() {
+ const statusDiv = document.getElementById('status');
+ statusDiv.textContent = 'Fetching Link Token...';
+ console.log('Initiating Link Token request.');
+
+ fetch('/api/plaid/create-link-token', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({
+ clientName: "Osiris",
+ countryCodes: ["US", "CA"],
+ language: "en",
+ userClientId: email,
+ products: ["transactions"]
+ })
+ })
+ .then(response => {
+ console.log('Received response for Link Token:', response);
+ if (!response.ok) {
+ throw new Error('Failed to fetch Link Token: ' + response.statusText);
+ }
+ return response.json();
+ })
+ .then(data => {
+ console.log('Link Token Data:', data);
+ const linkToken = data.linkToken;
+ statusDiv.textContent = 'Initializing Plaid Link...';
+ initializePlaidLink(linkToken);
+ })
+ .catch(error => {
+ console.error('Error fetching Link Token:', error);
+ statusDiv.textContent = 'Error fetching Link Token.';
+ });
+});
+
+
+/**
+ * Initializes Plaid Link with the provided Link Token.
+ * @param {string} linkToken - The Link Token obtained from the backend.
+ */
+function initializePlaidLink(linkToken) {
+ const statusDiv = document.getElementById('status');
+
+ const handler = Plaid.create({
+ token: linkToken,
+ onSuccess: function(public_token, metadata) {
+ console.log('Plaid Link Success. Public Token:', public_token);
+ console.log('Metadata:', metadata);
+ statusDiv.textContent = 'Exchanging Public Token...';
+ exchangePublicToken(public_token);
+ },
+ onExit: function(err, metadata) {
+ if (err != null) {
+ console.error('Plaid Link error:', err);
+ statusDiv.textContent = 'Error during Plaid Link.';
+ } else {
+ console.log('Plaid Link exited without linking.');
+ statusDiv.textContent = 'Plaid Link exited without linking.';
+ }
+ }
+ });
+
+ handler.open();
+}
+
+/**
+ * Sends the Public Token to the backend to exchange for an Access Token.
+ * @param {string} publicToken - The Public Token received from Plaid Link.
+ */
+function exchangePublicToken(publicToken) {
+ const statusDiv = document.getElementById('status');
+
+ fetch('/api/plaid/exchange-public-token', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({
+ publicToken: publicToken,
+ email: email
+ })
+ })
+ .then(response => {
+ console.log('Received response for Exchange Token:', response);
+ if (!response.ok) {
+ throw new Error('Failed to exchange Public Token: ' + response.statusText);
+ }
+ return response.json();
+ })
+ .then(data => {
+ console.log('Exchange Token Data:', data);
+ const accessToken = data.access_token;
+ const itemId = data.item_id;
+ console.log('Access Token:', accessToken);
+ console.log('Item ID:', itemId);
+ statusDiv.textContent = 'Bank account linked successfully!';
+ })
+ .catch(error => {
+ console.error('Error exchanging Public Token:', error);
+ statusDiv.textContent = 'Error exchanging Public Token.';
+ });
+}
diff --git a/src/main/resources/static/index.html b/src/main/resources/static/index.html
new file mode 100644
index 000000000..bb75ef751
--- /dev/null
+++ b/src/main/resources/static/index.html
@@ -0,0 +1,33 @@
+
+
+
+
+
+ Plaid Integration
+
+
+
+
+
+
Connect Your Bank Account
+
+
+
+
+
+
diff --git a/src/test/java/use_case/grabtransactions/GrabTransactionsInteractorTest.java b/src/test/java/use_case/grabtransactions/GrabTransactionsInteractorTest.java
new file mode 100644
index 000000000..d82b6ece3
--- /dev/null
+++ b/src/test/java/use_case/grabtransactions/GrabTransactionsInteractorTest.java
@@ -0,0 +1,97 @@
+package use_case.grabtransactions;
+
+import osiris.data_access.PlaidDataAccessObject;
+import osiris.data_access.DBUserDataAccessObject;
+import osiris.entity.User;
+import osiris.use_case.grabtransactions.GrabTransactionsInputData;
+import osiris.use_case.grabtransactions.GrabTransactionsInteractor;
+import osiris.use_case.grabtransactions.GrabTransactionOutputData;
+
+import osiris.utility.exceptions.PlaidUseCaseException;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+import java.io.IOException;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+public class GrabTransactionsInteractorTest {
+
+ private GrabTransactionsInteractor interactor;
+ private DBUserDataAccessObject userDataAccessObject;
+ private PlaidDataAccessObject plaidDao;
+
+ @BeforeEach
+ public void setUp() {
+ userDataAccessObject = mock(DBUserDataAccessObject.class);
+ plaidDao = mock(PlaidDataAccessObject.class);
+ interactor = new GrabTransactionsInteractor(userDataAccessObject, plaidDao);
+ }
+
+ @Test
+ public void testFetchTransactionsSuccess() throws IOException, PlaidUseCaseException {
+ // Arrange
+ String email = "validUser@example.com";
+ String accessToken = "validAccessToken";
+ String mockTransactions = "mockTransactions";
+
+ GrabTransactionsInputData inputData = new GrabTransactionsInputData(email);
+ User mockUser = mock(User.class);
+
+ when(mockUser.getAccessCode()).thenReturn(accessToken);
+ when(userDataAccessObject.get(email)).thenReturn(mockUser);
+ when(plaidDao.fetchTransactions(accessToken)).thenReturn(mockTransactions);
+
+ // Act
+ GrabTransactionOutputData outputData = interactor.fetchTransactions(inputData);
+
+ // Assert
+ assertNotNull(outputData);
+ assertEquals(mockTransactions, outputData.getTransactions());
+ verify(userDataAccessObject, times(1)).get(email);
+ verify(plaidDao, times(1)).fetchTransactions(accessToken);
+ }
+
+ @Test
+ public void testFetchTransactionsUserNotFound() throws IOException {
+ // Arrange
+ String email = "unknownUser@example.com";
+ GrabTransactionsInputData inputData = new GrabTransactionsInputData(email);
+
+ when(userDataAccessObject.get(email)).thenReturn(null);
+
+ // Act & Assert
+ Exception exception = assertThrows(PlaidUseCaseException.class, () -> {
+ interactor.fetchTransactions(inputData);
+ });
+
+ assertEquals("User not found: " + email, exception.getMessage());
+ verify(userDataAccessObject, times(1)).get(email);
+ verify(plaidDao, never()).fetchTransactions(anyString());
+ }
+
+ @Test
+ public void testFetchTransactionsPlaidError() throws IOException {
+ // Arrange
+ String email = "validUser@example.com";
+ String accessToken = "validAccessToken";
+
+ GrabTransactionsInputData inputData = new GrabTransactionsInputData(email);
+ User mockUser = mock(User.class);
+
+ when(mockUser.getAccessCode()).thenReturn(accessToken);
+ when(userDataAccessObject.get(email)).thenReturn(mockUser);
+ when(plaidDao.fetchTransactions(accessToken)).thenThrow(new IOException("Plaid API error"));
+
+ // Act & Assert
+ Exception exception = assertThrows(IOException.class, () -> {
+ interactor.fetchTransactions(inputData);
+ });
+
+ assertEquals("IO Error while creating transactions", exception.getMessage());
+ verify(userDataAccessObject, times(1)).get(email);
+ verify(plaidDao, times(1)).fetchTransactions(accessToken);
+ }
+}
diff --git a/src/test/java/use_case/login/LoginInteractorTest.java b/src/test/java/use_case/login/LoginInteractorTest.java
index 9fc1bfc89..e69de29bb 100644
--- a/src/test/java/use_case/login/LoginInteractorTest.java
+++ b/src/test/java/use_case/login/LoginInteractorTest.java
@@ -1,124 +0,0 @@
-package use_case.login;
-
-import data_access.InMemoryUserDataAccessObject;
-import entity.CommonUserFactory;
-import entity.User;
-import entity.UserFactory;
-import org.junit.jupiter.api.Test;
-
-import java.time.LocalDateTime;
-
-import static org.junit.jupiter.api.Assertions.*;
-
-class LoginInteractorTest {
-
- @Test
- void successTest() {
- LoginInputData inputData = new LoginInputData("Paul", "password");
- LoginUserDataAccessInterface userRepository = new InMemoryUserDataAccessObject();
-
- // For the success test, we need to add Paul to the data access repository before we log in.
- UserFactory factory = new CommonUserFactory();
- User user = factory.create("Paul", "password");
- userRepository.save(user);
-
- // This creates a successPresenter that tests whether the test case is as we expect.
- LoginOutputBoundary successPresenter = new LoginOutputBoundary() {
- @Override
- public void prepareSuccessView(LoginOutputData user) {
- assertEquals("Paul", user.getUsername());
- }
-
- @Override
- public void prepareFailView(String error) {
- fail("Use case failure is unexpected.");
- }
- };
-
- LoginInputBoundary interactor = new LoginInteractor(userRepository, successPresenter);
- interactor.execute(inputData);
- }
-
- @Test
- void successUserLoggedInTest() {
- LoginInputData inputData = new LoginInputData("Paul", "password");
- LoginUserDataAccessInterface userRepository = new InMemoryUserDataAccessObject();
-
- // For the success test, we need to add Paul to the data access repository before we log in.
- UserFactory factory = new CommonUserFactory();
- User user = factory.create("Paul", "password");
- userRepository.save(user);
-
- // This creates a successPresenter that tests whether the test case is as we expect.
- LoginOutputBoundary successPresenter = new LoginOutputBoundary() {
- @Override
- public void prepareSuccessView(LoginOutputData user) {
- assertEquals("Paul", userRepository.getCurrentUsername());
- }
-
- @Override
- public void prepareFailView(String error) {
- fail("Use case failure is unexpected.");
- }
- };
-
- LoginInputBoundary interactor = new LoginInteractor(userRepository, successPresenter);
- assertEquals(null, userRepository.getCurrentUsername());
-
- interactor.execute(inputData);
- }
-
- @Test
- void failurePasswordMismatchTest() {
- LoginInputData inputData = new LoginInputData("Paul", "wrong");
- LoginUserDataAccessInterface userRepository = new InMemoryUserDataAccessObject();
-
- // For this failure test, we need to add Paul to the data access repository before we log in, and
- // the passwords should not match.
- UserFactory factory = new CommonUserFactory();
- User user = factory.create("Paul", "password");
- userRepository.save(user);
-
- // This creates a presenter that tests whether the test case is as we expect.
- LoginOutputBoundary failurePresenter = new LoginOutputBoundary() {
- @Override
- public void prepareSuccessView(LoginOutputData user) {
- // this should never be reached since the test case should fail
- fail("Use case success is unexpected.");
- }
-
- @Override
- public void prepareFailView(String error) {
- assertEquals("Incorrect password for \"Paul\".", error);
- }
- };
-
- LoginInputBoundary interactor = new LoginInteractor(userRepository, failurePresenter);
- interactor.execute(inputData);
- }
-
- @Test
- void failureUserDoesNotExistTest() {
- LoginInputData inputData = new LoginInputData("Paul", "password");
- LoginUserDataAccessInterface userRepository = new InMemoryUserDataAccessObject();
-
- // Add Paul to the repo so that when we check later they already exist
-
- // This creates a presenter that tests whether the test case is as we expect.
- LoginOutputBoundary failurePresenter = new LoginOutputBoundary() {
- @Override
- public void prepareSuccessView(LoginOutputData user) {
- // this should never be reached since the test case should fail
- fail("Use case success is unexpected.");
- }
-
- @Override
- public void prepareFailView(String error) {
- assertEquals("Paul: Account does not exist.", error);
- }
- };
-
- LoginInputBoundary interactor = new LoginInteractor(userRepository, failurePresenter);
- interactor.execute(inputData);
- }
-}
\ No newline at end of file
diff --git a/src/test/java/use_case/logout/LogoutInteractorTest.java b/src/test/java/use_case/logout/LogoutInteractorTest.java
index b005a402c..e69de29bb 100644
--- a/src/test/java/use_case/logout/LogoutInteractorTest.java
+++ b/src/test/java/use_case/logout/LogoutInteractorTest.java
@@ -1,44 +0,0 @@
-package use_case.logout;
-
-import data_access.InMemoryUserDataAccessObject;
-import entity.CommonUserFactory;
-import entity.User;
-import entity.UserFactory;
-import org.junit.jupiter.api.Test;
-
-import static org.junit.jupiter.api.Assertions.*;
-
-class LogoutInteractorTest {
-
- @Test
- void successTest() {
- LogoutInputData inputData = new LogoutInputData("Paul");
- InMemoryUserDataAccessObject userRepository = new InMemoryUserDataAccessObject();
-
- // For the success test, we need to add Paul to the data access repository before we log in.
- UserFactory factory = new CommonUserFactory();
- User user = factory.create("Paul", "password");
- userRepository.save(user);
- userRepository.setCurrentUsername("Paul");
-
- // This creates a successPresenter that tests whether the test case is as we expect.
- LogoutOutputBoundary successPresenter = new LogoutOutputBoundary() {
- @Override
- public void prepareSuccessView(LogoutOutputData user) {
- // check that the output data contains the username of who logged out
- assertEquals("Paul", user.getUsername());
- }
-
- @Override
- public void prepareFailView(String error) {
- fail("Use case failure is unexpected.");
- }
- };
-
- LogoutInputBoundary interactor = new LogoutInteractor(userRepository, successPresenter);
- interactor.execute(inputData);
- // check that the user was logged out
- assertNull(userRepository.getCurrentUsername());
- }
-
-}
\ No newline at end of file
diff --git a/src/test/java/use_case/plaid/PlaidInteractorTest.java b/src/test/java/use_case/plaid/PlaidInteractorTest.java
new file mode 100644
index 000000000..54c27acb0
--- /dev/null
+++ b/src/test/java/use_case/plaid/PlaidInteractorTest.java
@@ -0,0 +1,220 @@
+package use_case.plaid;
+
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import osiris.data_access.PlaidDataAccessObject.ExchangeTokenResponse;
+import osiris.data_access.PlaidDataAccessObject.LinkTokenResponse;
+import osiris.entity.CommonUserFactory;
+import osiris.entity.User;
+import osiris.entity.UserFactory;
+import osiris.use_case.plaid.*;
+import osiris.utility.exceptions.PlaidException;
+import osiris.utility.exceptions.PlaidUseCaseException;
+
+import java.io.IOException;
+import java.sql.SQLException;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+class PlaidInteractorTest {
+
+ private static UserPlaidDataAccessInterface mockPlaidDao;
+ private static PlaidDataBaseUserAccessObjectInterface mockUserRepository;
+ private static UserFactory mockUserFactory;
+ private static PlaidInteractor interactor;
+
+ @BeforeAll
+ static void setUp() {
+ mockPlaidDao = mock(UserPlaidDataAccessInterface.class);
+ mockUserRepository = mock(PlaidDataBaseUserAccessObjectInterface.class);
+ mockUserFactory = mock(UserFactory.class);
+
+ interactor = new PlaidInteractor(mockPlaidDao, mockUserRepository, mockUserFactory);
+ }
+
+ @BeforeEach
+ void resetMocks() {
+ reset(mockPlaidDao, mockUserRepository, mockUserFactory);
+ }
+
+ @Test
+ void testCreateLinkTokenSuccess() throws Exception {
+ CreateLinkTokenInputData inputData = new CreateLinkTokenInputData(
+ "TestApp",
+ new String[]{"US"},
+ "en",
+ "test-client-id",
+ new String[]{"auth", "transactions"}
+ );
+
+ LinkTokenResponse mockResponse = new LinkTokenResponse("test-link-token", "test-request-id");
+ when(mockPlaidDao.createLinkToken(any(), any(), any(), any(), any())).thenReturn(mockResponse);
+
+ CreateLinkTokenOutputData result = interactor.createLinkToken(inputData);
+
+ assertEquals("test-link-token", result.getLinkToken());
+ assertEquals("test-request-id", result.getRequestId());
+ verify(mockPlaidDao, times(1)).createLinkToken(
+ eq("TestApp"), eq(new String[]{"US"}), eq("en"),
+ eq("test-client-id"), eq(new String[]{"auth", "transactions"})
+ );
+ }
+
+ @Test
+ void testCreateLinkTokenPlaidException() throws Exception {
+ CreateLinkTokenInputData inputData = new CreateLinkTokenInputData(
+ "TestApp",
+ new String[]{"US"},
+ "en",
+ "test-client-id",
+ new String[]{"auth", "transactions"}
+ );
+
+ when(mockPlaidDao.createLinkToken(any(), any(), any(), any(), any()))
+ .thenThrow(new PlaidException("Plaid API Error"));
+
+ PlaidUseCaseException exception = assertThrows(PlaidUseCaseException.class, () -> {
+ interactor.createLinkToken(inputData);
+ });
+
+ assertTrue(exception.getMessage().contains("Failed to create Link Token"));
+ verify(mockPlaidDao, times(1)).createLinkToken(any(), any(), any(), any(), any());
+ }
+
+ @Test
+ void testExchangePublicTokenSuccess() throws Exception {
+ ExchangePublicTokenInputData inputData = new ExchangePublicTokenInputData(
+ "test-public-token",
+ "test-client-id"
+ );
+
+ ExchangeTokenResponse mockResponse = new ExchangeTokenResponse(
+ "test-access-token", "test-item-id", "test-request-id"
+ );
+ User mockUser = new User() {
+ @Override
+ public String getEmail() {
+ return "";
+ }
+
+ @Override
+ public String getPassword() {
+ return "";
+ }
+
+ @Override
+ public String getAccessCode() {
+ return "";
+ }
+ };
+ User newUser = new User() {
+ @Override
+ public String getEmail() {
+ return "";
+ }
+
+ @Override
+ public String getPassword() {
+ return "";
+ }
+
+ @Override
+ public String getAccessCode() {
+ return "";
+ }
+ };
+
+ when(mockPlaidDao.exchangePublicToken(any())).thenReturn(mockResponse);
+ when(mockUserRepository.get(any())).thenReturn(mockUser);
+ when(mockUserFactory.create(any(), any(), any())).thenReturn(newUser);
+
+ ExchangePublicTokenOutputData result = interactor.exchangePublicToken(inputData);
+
+ assertEquals("test-access-token", result.getAccessToken());
+ assertEquals("test-item-id", result.getItemId());
+ assertEquals("test-request-id", result.getRequestId());
+
+ verify(mockUserRepository, times(1)).save(newUser);
+ }
+
+ @Test
+ void testExchangePublicTokenSQLException() throws IOException, PlaidException {
+ // Arrange: Create input data
+ ExchangePublicTokenInputData inputData = new ExchangePublicTokenInputData(
+ "test-public-token",
+ "test-client-id"
+ );
+
+ // Mock the response for exchangePublicToken
+ ExchangeTokenResponse mockResponse = new ExchangeTokenResponse(
+ "test-access-token", "test-item-id", "test-request-id"
+ );
+ when(mockPlaidDao.exchangePublicToken(any())).thenReturn(mockResponse);
+
+ // Mock RuntimeException wrapping SQLException
+ when(mockUserRepository.get(any()))
+ .thenThrow(new RuntimeException(new SQLException("Simulated database error")));
+
+ // Act & Assert: Ensure RuntimeException is thrown and contains the SQLException
+ RuntimeException exception = assertThrows(RuntimeException.class, () -> {
+ interactor.exchangePublicToken(inputData);
+ });
+
+ // Verify the cause of the RuntimeException is an SQLException
+ assertTrue(exception.getCause() instanceof SQLException);
+ assertEquals("Simulated database error", exception.getCause().getMessage());
+
+ // Verify interactions with mocks
+ verify(mockPlaidDao, times(1)).exchangePublicToken(eq("test-public-token"));
+ verify(mockUserRepository, times(1)).get(any());
+ }
+
+
+
+
+ @Test
+ void testExchangePublicTokenIOException() throws IOException, PlaidException {
+ ExchangePublicTokenInputData inputData = new ExchangePublicTokenInputData(
+ "test-public-token",
+ "test-client-id"
+ );
+
+ // Mock IOException
+ when(mockPlaidDao.exchangePublicToken(any()))
+ .thenThrow(new IOException("IO Error"));
+
+ // Assert PlaidUseCaseException is thrown
+ PlaidUseCaseException exception = assertThrows(PlaidUseCaseException.class, () -> {
+ interactor.exchangePublicToken(inputData);
+ });
+
+ // Assert exception message
+ assertTrue(exception.getMessage().contains("IO Error while exchanging Public Token"));
+ verify(mockPlaidDao, times(1)).exchangePublicToken(eq("test-public-token"));
+ }
+
+ @Test
+ void testExchangePublicTokenPlaidException() throws IOException, PlaidException {
+ ExchangePublicTokenInputData inputData = new ExchangePublicTokenInputData(
+ "test-public-token",
+ "test-client-id"
+ );
+
+ // Mock PlaidException
+ when(mockPlaidDao.exchangePublicToken(any()))
+ .thenThrow(new PlaidException("Plaid API Error"));
+
+ // Assert PlaidUseCaseException is thrown
+ PlaidUseCaseException exception = assertThrows(PlaidUseCaseException.class, () -> {
+ interactor.exchangePublicToken(inputData);
+ });
+
+ // Assert exception message
+ assertTrue(exception.getMessage().contains("Failed to exchange Public Token"));
+ verify(mockPlaidDao, times(1)).exchangePublicToken(eq("test-public-token"));
+ }
+
+
+}
diff --git a/src/test/java/use_case/signup/SignupInteractorTest.java b/src/test/java/use_case/signup/SignupInteractorTest.java
index 00f757c1c..e69de29bb 100644
--- a/src/test/java/use_case/signup/SignupInteractorTest.java
+++ b/src/test/java/use_case/signup/SignupInteractorTest.java
@@ -1,104 +0,0 @@
-package use_case.signup;
-
-import data_access.InMemoryUserDataAccessObject;
-import entity.CommonUserFactory;
-import entity.User;
-import entity.UserFactory;
-import org.junit.jupiter.api.Test;
-
-import java.time.LocalDateTime;
-
-import static org.junit.jupiter.api.Assertions.*;
-
-class SignupInteractorTest {
-
- @Test
- void successTest() {
- SignupInputData inputData = new SignupInputData("Paul", "password", "password");
- SignupUserDataAccessInterface userRepository = new InMemoryUserDataAccessObject();
-
- // This creates a successPresenter that tests whether the test case is as we expect.
- SignupOutputBoundary successPresenter = new SignupOutputBoundary() {
- @Override
- public void prepareSuccessView(SignupOutputData user) {
- // 2 things to check: the output data is correct, and the user has been created in the DAO.
- assertEquals("Paul", user.getUsername());
- assertTrue(userRepository.existsByName("Paul"));
- }
-
- @Override
- public void prepareFailView(String error) {
- fail("Use case failure is unexpected.");
- }
-
- @Override
- public void switchToLoginView() {
- // This is expected
- }
- };
-
- SignupInputBoundary interactor = new SignupInteractor(userRepository, successPresenter, new CommonUserFactory());
- interactor.execute(inputData);
- }
-
- @Test
- void failurePasswordMismatchTest() {
- SignupInputData inputData = new SignupInputData("Paul", "password", "wrong");
- SignupUserDataAccessInterface userRepository = new InMemoryUserDataAccessObject();
-
- // This creates a presenter that tests whether the test case is as we expect.
- SignupOutputBoundary failurePresenter = new SignupOutputBoundary() {
- @Override
- public void prepareSuccessView(SignupOutputData user) {
- // this should never be reached since the test case should fail
- fail("Use case success is unexpected.");
- }
-
- @Override
- public void prepareFailView(String error) {
- assertEquals("Passwords don't match.", error);
- }
-
- @Override
- public void switchToLoginView() {
- // This is expected
- }
- };
-
- SignupInputBoundary interactor = new SignupInteractor(userRepository, failurePresenter, new CommonUserFactory());
- interactor.execute(inputData);
- }
-
- @Test
- void failureUserExistsTest() {
- SignupInputData inputData = new SignupInputData("Paul", "password", "wrong");
- SignupUserDataAccessInterface userRepository = new InMemoryUserDataAccessObject();
-
- // Add Paul to the repo so that when we check later they already exist
- UserFactory factory = new CommonUserFactory();
- User user = factory.create("Paul", "pwd");
- userRepository.save(user);
-
- // This creates a presenter that tests whether the test case is as we expect.
- SignupOutputBoundary failurePresenter = new SignupOutputBoundary() {
- @Override
- public void prepareSuccessView(SignupOutputData user) {
- // this should never be reached since the test case should fail
- fail("Use case success is unexpected.");
- }
-
- @Override
- public void prepareFailView(String error) {
- assertEquals("User already exists.", error);
- }
-
- @Override
- public void switchToLoginView() {
- // This is expected
- }
- };
-
- SignupInputBoundary interactor = new SignupInteractor(userRepository, failurePresenter, new CommonUserFactory());
- interactor.execute(inputData);
- }
-}
\ No newline at end of file
diff --git a/src/test/java/use_case/viewexpenses/ViewExpensesInteractorTest.java b/src/test/java/use_case/viewexpenses/ViewExpensesInteractorTest.java
new file mode 100644
index 000000000..424e726c6
--- /dev/null
+++ b/src/test/java/use_case/viewexpenses/ViewExpensesInteractorTest.java
@@ -0,0 +1,98 @@
+package use_case.viewexpenses;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonObject;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+import osiris.use_case.viewexpenses.ViewExpensesInputData;
+import osiris.use_case.viewexpenses.ViewExpensesInteractor;
+import osiris.use_case.viewexpenses.ViewExpensesOutputBoundary;
+
+import static org.mockito.Mockito.*;
+
+class ViewExpensesInteractorTest {
+
+ private ViewExpensesOutputBoundary presenter;
+ private ViewExpensesInteractor interactor;
+
+ @BeforeEach
+ void setUp() {
+ presenter = mock(ViewExpensesOutputBoundary.class);
+ interactor = new ViewExpensesInteractor(presenter);
+ }
+
+ @Test
+ void testExecuteWithEssentialAndNonEssentialTransactions() {
+ // Arrange
+ String transactionsJson = createMockTransactionsJson();
+ ViewExpensesInputData inputData = new ViewExpensesInputData(transactionsJson);
+
+ // Act
+ interactor.execute(inputData);
+
+ // Assert
+ verify(presenter, times(1)).prepareChart(argThat(outputData ->
+ outputData.getEssentialTotal() == 80.0 && outputData.getNonEssentialTotal() == 40.0));
+ }
+
+ @Test
+ void testExecuteWithNoTransactions() {
+ // Arrange
+ String transactionsJson = "{\"transactions\":[]}";
+ ViewExpensesInputData inputData = new ViewExpensesInputData(transactionsJson);
+
+ // Act
+ interactor.execute(inputData);
+
+ // Assert
+ verify(presenter, times(1)).prepareChart(argThat(outputData ->
+ outputData.getEssentialTotal() == 0.0 && outputData.getNonEssentialTotal() == 0.0));
+ }
+
+ @Test
+ void testSwitchToHomeView() {
+ // Act
+ interactor.switchToHomeView();
+
+ // Assert
+ verify(presenter, times(1)).switchToHomeView();
+ }
+
+ /**
+ * Helper method to create mock JSON transactions.
+ */
+ private String createMockTransactionsJson() {
+ JsonArray transactions = new JsonArray();
+
+ // Essential transaction
+ JsonObject essentialTransaction = new JsonObject();
+ essentialTransaction.addProperty("amount", 50.0);
+ JsonArray essentialCategory = new JsonArray();
+ essentialCategory.add("Groceries");
+ essentialTransaction.add("category", essentialCategory);
+ transactions.add(essentialTransaction);
+
+ // Another essential transaction
+ JsonObject anotherEssentialTransaction = new JsonObject();
+ anotherEssentialTransaction.addProperty("amount", 30.0);
+ JsonArray anotherEssentialCategory = new JsonArray();
+ anotherEssentialCategory.add("Rent");
+ anotherEssentialTransaction.add("category", anotherEssentialCategory);
+ transactions.add(anotherEssentialTransaction);
+
+ // Non-essential transaction
+ JsonObject nonEssentialTransaction = new JsonObject();
+ nonEssentialTransaction.addProperty("amount", 40.0);
+ JsonArray nonEssentialCategory = new JsonArray();
+ nonEssentialCategory.add("Entertainment");
+ nonEssentialTransaction.add("category", nonEssentialCategory);
+ transactions.add(nonEssentialTransaction);
+
+ // Wrap in JSON response
+ JsonObject response = new JsonObject();
+ response.add("transactions", transactions);
+
+ return response.toString();
+ }
+}