diff --git a/.github/workflows/main2.yml b/.github/workflows/main2.yml index d5b541c9..f438854f 100644 --- a/.github/workflows/main2.yml +++ b/.github/workflows/main2.yml @@ -53,6 +53,12 @@ jobs: run: flutter test working-directory: client + - name: Decode keystore file + env: + KEYSTORE: ${{ secrets.KEYSTORE }} + run: | + echo $KEYSTORE | base64 --decode > /home/runner/work/Koja/Koja/client/android/app/mykey.jks + - name: Build Flutter app run: flutter build apk working-directory: client diff --git a/README.md b/README.md index 6886541f..079011c2 100644 --- a/README.md +++ b/README.md @@ -5,33 +5,39 @@ [![Repo Size](https://img.shields.io/github/repo-size/COS301-SE-2023/Koja.svg)](https://github.com/COS301-SE-2023/Koja) [![Koja Build Status](https://github.com/COS301-SE-2023/Koja/actions/workflows/main2.yml/badge.svg)](https://github.com/COS301-SE-2023/Koja/actions/workflows/main2.yml) + +

+ Koja Icon +

+ ## Project Description: Koja is a system that enables more efficient time utilisation through dynamic and fixed time allocation for specific tasks a user wants to complete. Koja's aim is to minimise solve the burden of schedule management while requiring minimal effort from the user. +# Advertisement +https://github.com/COS301-SE-2023/Koja/assets/105129419/1d39ca2c-c081-4534-94d4-7558d399c484 + # Documentation * [SRSv4](https://www.canva.com/design/DAFm6EdVuUo/d1PDPN7l5k5e_cx3V7UT1A/edit?utm_content=DAFm6EdVuUo&utm_campaign=designshare&utm_medium=link2&utm_source=sharebutton) * [Project Board](https://github.com/orgs/COS301-SE-2023/projects/37) -## Demo Videos -* [Team Caffeine-Demo1](https://drive.google.com/file/d/165ckgDKdO0YbYHQ_8qtw9sJ56FZ6jSGC/view?usp=sharing) +## Demo Slide & Video +* [Team Caffeine - Demo Slide](https://www.canva.com/design/DAFuI_vIphg/Ux_tSd2K-evrvKIDnCR8PA/edit?utm_content=DAFuI_vIphg&utm_campaign=designshare&utm_medium=link2&utm_source=sharebutton) -* [Team Caffeine-Demo2](https://www.canva.com/design/DAFmn-ECx6w/1bhJhvtIjHPHMSG7kXUJhA/edit?utmcontent=DAFmn-ECx6w&utm_campaign=designshare&utm_medium=link2&utm_source=sharebutton) +* [Team Caffeine - Demo Video](https://drive.google.com/file/d/1HWAUgzdQUKvIi33rHqagadPkk6OxEqA-/view?usp=drive_link) -* [Team Caffeine-Demo3](https://www.canva.com/design/DAFoP47g3sw/_QjN9Yd8liAkDbpPl9SBhg/edit?utm_content=DAFoP47g3sw&utm_campaign=designshare&utm_medium=link2&utm_source=sharebutton) - -* [Team Caffeine-Demo4](https://www.canva.com/design/DAFuI_vIphg/Ux_tSd2K-evrvKIDnCR8PA/edit?utm_content=DAFuI_vIphg&utm_campaign=designshare&utm_medium=link2&utm_source=sharebutton) ## Additional Documentations * [Technical Installation Manual](https://docs.google.com/document/d/1pdzUM2YM_lkqxtvTfs2MToCcu3zSB6QwQbP5pfhyjHk/edit?usp=sharing) * [User Manual](https://docs.google.com/presentation/d/1cPbn8JxNLQUwRQt_2Euw9KsLvmQ2d15Y-28ukS4Yh1A/edit?usp=drive_link) -* [Contributions](https://docs.google.com/document/d/1OVnDnd-1888-pUsX0dXAO_8Iy7R1oDDPmhMQqGZD8TI/edit?usp=sharing) - * [Coding Standards](https://docs.google.com/document/d/1jZFns50dd7gQlAGJpq--38faijdhuxrOiWK_JtvKXQI/edit?usp=sharing) * [Architectural Design](https://docs.google.com/document/d/1hVQcrOcnCRzHyd9Y-aXYuAfyRZxVVZTiJyluJbr_0RY/edit?usp=sharing) +* [Testing Policy Document](https://docs.google.com/document/d/10JTOApaAbRHdl4zoTkm39UPMgZYwPrVCrqgX5lBN5EA/edit?usp=sharing) +# Technologies +* Flutter, SpringBoot, Kotlin, Amazon RDS, Amazon DynamoDB # Team 📧 Team email: [teamcaffeine23@gmail.com](mailto:teamcaffeine23@gmail.com) diff --git a/client/analysis_options.yaml b/client/analysis_options.yaml index e8c38882..954f5df4 100644 --- a/client/analysis_options.yaml +++ b/client/analysis_options.yaml @@ -1,6 +1,3 @@ - -include: package:lints/recommended.yaml - linter: rules: avoid_print: true \ No newline at end of file diff --git a/client/android/app/build.gradle b/client/android/app/build.gradle index f72e47c1..fdb93a67 100644 --- a/client/android/app/build.gradle +++ b/client/android/app/build.gradle @@ -45,7 +45,7 @@ android { defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId "com.example.client" + applicationId "com.teamcaffeine.koja" // You can update the following values to match your application needs. // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. minSdkVersion 21 @@ -67,7 +67,7 @@ android { release { // TODO: Add your own signing config for the release build. // Signing with the debug keys for now, so `flutter run --release` works. - // signingConfig signingConfigs.debug + signingConfig signingConfigs.debug } debug{ diff --git a/client/android/app/src/main/AndroidManifest.xml b/client/android/app/src/main/AndroidManifest.xml index 19a809b6..5618cba7 100644 --- a/client/android/app/src/main/AndroidManifest.xml +++ b/client/android/app/src/main/AndroidManifest.xml @@ -1,6 +1,6 @@ - + + + + diff --git a/client/android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png b/client/android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png new file mode 100644 index 00000000..4f932767 Binary files /dev/null and b/client/android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png differ diff --git a/client/android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png b/client/android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png new file mode 100644 index 00000000..175f821b Binary files /dev/null and b/client/android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png differ diff --git a/client/android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png b/client/android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png new file mode 100644 index 00000000..6486158e Binary files /dev/null and b/client/android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png differ diff --git a/client/android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png b/client/android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png new file mode 100644 index 00000000..6d58a66e Binary files /dev/null and b/client/android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png differ diff --git a/client/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png b/client/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png new file mode 100644 index 00000000..ab698e2f Binary files /dev/null and b/client/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png differ diff --git a/client/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/client/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 00000000..5f349f7f --- /dev/null +++ b/client/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/client/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/client/android/app/src/main/res/mipmap-hdpi/ic_launcher.png index db77bb4b..2ad368a9 100644 Binary files a/client/android/app/src/main/res/mipmap-hdpi/ic_launcher.png and b/client/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/client/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/client/android/app/src/main/res/mipmap-mdpi/ic_launcher.png index 17987b79..de8d8066 100644 Binary files a/client/android/app/src/main/res/mipmap-mdpi/ic_launcher.png and b/client/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/client/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/client/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png index 09d43914..99eeb4c3 100644 Binary files a/client/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png and b/client/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/client/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/client/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png index d5f1c8d3..f70219ff 100644 Binary files a/client/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and b/client/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/client/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/client/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png index 4d6372ee..c115aeab 100644 Binary files a/client/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and b/client/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/client/android/app/src/main/res/values/colors.xml b/client/android/app/src/main/res/values/colors.xml new file mode 100644 index 00000000..a26c650a --- /dev/null +++ b/client/android/app/src/main/res/values/colors.xml @@ -0,0 +1,4 @@ + + + #6B1DE2 + \ No newline at end of file diff --git a/client/assets/icons/coffee.png b/client/assets/icons/coffee.png deleted file mode 100644 index 14cd46a0..00000000 Binary files a/client/assets/icons/coffee.png and /dev/null differ diff --git a/client/assets/icons/google.png b/client/assets/icons/google.png deleted file mode 100644 index e7d106c4..00000000 Binary files a/client/assets/icons/google.png and /dev/null differ diff --git a/client/assets/icons/google1.png b/client/assets/icons/google1.png deleted file mode 100644 index 894ca6aa..00000000 Binary files a/client/assets/icons/google1.png and /dev/null differ diff --git a/client/assets/icons/koja1.png b/client/assets/icons/koja1.png deleted file mode 100644 index 2e3ad29c..00000000 Binary files a/client/assets/icons/koja1.png and /dev/null differ diff --git a/client/assets/icons/koja_foreground.png b/client/assets/icons/koja_foreground.png new file mode 100644 index 00000000..16051c07 Binary files /dev/null and b/client/assets/icons/koja_foreground.png differ diff --git a/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png index dc9ada47..957b0a48 100644 Binary files a/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png and b/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png differ diff --git a/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png index 7353c41e..b8e78cd8 100644 Binary files a/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png and b/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ diff --git a/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png index 797d452e..d2d8468c 100644 Binary files a/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png and b/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ diff --git a/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png index 6ed2d933..ae7de895 100644 Binary files a/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png and b/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ diff --git a/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png index 4cd7b009..4b40c2c6 100644 Binary files a/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png and b/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ diff --git a/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png index fe730945..b4b24258 100644 Binary files a/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png and b/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ diff --git a/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png index 321773cd..ff60d05f 100644 Binary files a/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png and b/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ diff --git a/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png index 797d452e..d2d8468c 100644 Binary files a/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png and b/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ diff --git a/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png index 502f463a..92648387 100644 Binary files a/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png and b/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ diff --git a/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png index 0ec30343..2f17ab10 100644 Binary files a/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png and b/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ diff --git a/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png b/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png new file mode 100644 index 00000000..8c0b6b08 Binary files /dev/null and b/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png differ diff --git a/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png b/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png new file mode 100644 index 00000000..f3c60d3a Binary files /dev/null and b/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png differ diff --git a/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png b/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png new file mode 100644 index 00000000..cf45c28c Binary files /dev/null and b/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png differ diff --git a/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png b/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png new file mode 100644 index 00000000..724cb0b1 Binary files /dev/null and b/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png differ diff --git a/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png index 0ec30343..2f17ab10 100644 Binary files a/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png and b/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ diff --git a/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png index e9f5fea2..7a07b72e 100644 Binary files a/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png and b/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ diff --git a/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png b/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png new file mode 100644 index 00000000..7494288a Binary files /dev/null and b/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png differ diff --git a/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png b/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png new file mode 100644 index 00000000..aa8d4116 Binary files /dev/null and b/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png differ diff --git a/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png index 84ac32ae..fad9bff3 100644 Binary files a/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png and b/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ diff --git a/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png index 8953cba0..5e9b1b06 100644 Binary files a/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png and b/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ diff --git a/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png index 0467bf12..2d794120 100644 Binary files a/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png and b/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ diff --git a/client/ios/Runner/Info.plist b/client/ios/Runner/Info.plist index daa8eafc..5abf9f6d 100644 --- a/client/ios/Runner/Info.plist +++ b/client/ios/Runner/Info.plist @@ -7,7 +7,7 @@ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleDisplayName - Client + Koja CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier @@ -15,7 +15,7 @@ CFBundleInfoDictionaryVersion 6.0 CFBundleName - client + Koja CFBundlePackageType APPL CFBundleShortVersionString diff --git a/client/lib/Utils/environment_variable_constants.dart b/client/lib/Utils/environment_variable_constants.dart index b81de094..3989a20d 100644 --- a/client/lib/Utils/environment_variable_constants.dart +++ b/client/lib/Utils/environment_variable_constants.dart @@ -1,20 +1,5 @@ class EnvironmentVariableConstant { - static const String kojaAwsRdsDatabaseUrl = "KOJA_AWS_RDS_DATABASE_URL"; - static const String kojaAwsRdsDatabaseAdminUsername = "KOJA_AWS_RDS_DATABASE_ADMIN_USERNAME"; - static const String kojaAwsRdsDatabaseAdminPassword = "KOJA_AWS_RDS_DATABASE_ADMIN_PASSWORD"; - static const String kojaAwsDynamodbAccessKeyId = "KOJA_AWS_DYNAMODB_ACCESS_KEY_ID"; - static const String kojaAwsDynamodbAccessKeySecret = "KOJA_AWS_DYNAMODB_ACCESS_KEY_SECRET"; - static const String googleClientId = "GOOGLE_CLIENT_ID"; - static const String googleClientSecret = "GOOGLE_CLIENT_SECRET"; - static const String googleMapsApiKey = "GOOGLE_MAPS_API_KEY"; - static const String kojaJwtSecret = "KOJA_JWT_SECRET"; - static const String openaiApiKey = "OPENAI_API_KEY"; - static const String kojaIdSecret = "KOJA_ID_SECRET"; - static const String coverallsRepoToken = "COVERALLS_REPO_TOKEN"; - static const String kojaPrivateKeyPass = "KOJA_PRIVATE_KEY_PASS"; - static const String kojaPrivateKeySalt = "KOJA_PRIVATE_KEY_SALT"; - static const String aiPrivateKeyPass = "AI_PRIVATE_KEY_PASS"; - static const String aiPrivateKeySalt = "AI_PRIVATE_KEY_SALT"; - static const String serverAddress = "SERVER_ADDRESS"; - static const String serverPort = "SERVER_PORT"; + static const String googleMapsApiKey = "koja_google_maps_api_key"; + static const String serverAddress = "koja_server_address"; + static const String serverPort = "koja_server_port"; } \ No newline at end of file diff --git a/client/lib/assets/icons/artificial-intelligence.png b/client/lib/assets/icons/artificial-intelligence.png deleted file mode 100644 index 79192193..00000000 Binary files a/client/lib/assets/icons/artificial-intelligence.png and /dev/null differ diff --git a/client/lib/assets/icons/computer.png b/client/lib/assets/icons/computer.png deleted file mode 100644 index 6dc55cb0..00000000 Binary files a/client/lib/assets/icons/computer.png and /dev/null differ diff --git a/client/lib/assets/icons/destination.png b/client/lib/assets/icons/destination.png deleted file mode 100644 index 4e92fba1..00000000 Binary files a/client/lib/assets/icons/destination.png and /dev/null differ diff --git a/client/lib/assets/icons/google-calendar.png b/client/lib/assets/icons/google-calendar.png deleted file mode 100644 index 3ed934f0..00000000 Binary files a/client/lib/assets/icons/google-calendar.png and /dev/null differ diff --git a/client/lib/assets/icons/home.png b/client/lib/assets/icons/home.png deleted file mode 100644 index d59b92cc..00000000 Binary files a/client/lib/assets/icons/home.png and /dev/null differ diff --git a/client/lib/assets/icons/schedule-time.png b/client/lib/assets/icons/schedule-time.png deleted file mode 100644 index 9697a390..00000000 Binary files a/client/lib/assets/icons/schedule-time.png and /dev/null differ diff --git a/client/lib/assets/icons/schedule.png b/client/lib/assets/icons/schedule.png deleted file mode 100644 index 05ac6724..00000000 Binary files a/client/lib/assets/icons/schedule.png and /dev/null differ diff --git a/client/lib/assets/icons/stress.png b/client/lib/assets/icons/stress.png deleted file mode 100644 index 126c3e98..00000000 Binary files a/client/lib/assets/icons/stress.png and /dev/null differ diff --git a/client/lib/assets/icons/user.png b/client/lib/assets/icons/user.png deleted file mode 100644 index 39ce13b7..00000000 Binary files a/client/lib/assets/icons/user.png and /dev/null differ diff --git a/client/lib/providers/service_provider.dart b/client/lib/providers/service_provider.dart index b750bb54..50129f07 100644 --- a/client/lib/providers/service_provider.dart +++ b/client/lib/providers/service_provider.dart @@ -158,7 +158,7 @@ class ServiceProvider with ChangeNotifier { } /// This function will attempt to add another email using UserAccountController - Future addEmail({required ContextProvider eventProvider}) async { + Future addEmail({required ContextProvider eventProvider}) async { final String authUrl = '$_serverAddress:$_serverPort/api/v1/user/auth/add-email/google?token=$_accessToken'; @@ -169,11 +169,16 @@ class ServiceProvider with ChangeNotifier { callbackUrlScheme: callbackUrlScheme, ); - response = Uri.parse(response).queryParameters['token']; + final parsedResponse = Uri.parse(response).queryParameters; - setAccessToken(response, eventProvider); - - return accessToken != null; + if(parsedResponse.containsKey("token")) { + setAccessToken(parsedResponse["token"], eventProvider); + return true; + } + else if(parsedResponse.containsKey("error")) + { + return null; + } else return false; } /// This function will attempt to delete an email from the user's account diff --git a/client/lib/screens/suggestions_screens.dart b/client/lib/screens/suggestions_screens.dart index a82bf9b1..de540c6d 100644 --- a/client/lib/screens/suggestions_screens.dart +++ b/client/lib/screens/suggestions_screens.dart @@ -3,6 +3,7 @@ import 'package:koja/Utils/event_util.dart'; import 'package:koja/providers/context_provider.dart'; import 'package:provider/provider.dart'; import 'package:syncfusion_flutter_calendar/calendar.dart'; +import 'package:lottie/lottie.dart'; import '../providers/service_provider.dart'; import '../widgets/calendar_widget.dart'; @@ -125,8 +126,34 @@ class _SuggestionsTasksScreenState extends State { } else { + // return Center( + // child: Text('Koja\'s suggestion engine is still learning.\nPlease try again soon.'), + // ); return Center( - child: Text('Koja\'s suggestion engine is still learning.\nPlease try again soon.'), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + alignment: Alignment.center, + child: Lottie.asset( + 'assets/animations/ai.json', + height: 200, + width: 300, + repeat: false, + ), + ), + Center( + child: Text( + 'Koja\'s suggestion engine is still learning.\nPlease try again soon.', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w500, + fontFamily: 'Raleway'), + textAlign: TextAlign.center, + ), + ), + ], + ), ); } } diff --git a/client/lib/widgets/about_us_widget.dart b/client/lib/widgets/about_us_widget.dart index 74a8a37a..6b2bf56c 100644 --- a/client/lib/widgets/about_us_widget.dart +++ b/client/lib/widgets/about_us_widget.dart @@ -53,7 +53,7 @@ class AboutUsWidget extends StatelessWidget { ], ), ), - Text("Version 0.0.4", + Text("Version 1.0.0", style: GoogleFonts.ubuntu( fontSize: 14, fontWeight: FontWeight.w400, diff --git a/client/lib/widgets/add_email_widget.dart b/client/lib/widgets/add_email_widget.dart index aaca83fd..b553273b 100644 --- a/client/lib/widgets/add_email_widget.dart +++ b/client/lib/widgets/add_email_widget.dart @@ -27,15 +27,17 @@ class _AddEmailModalState extends State { children: [ ElevatedButton( onPressed: () async { - if (await serviceProvider.addEmail( - eventProvider: eventProvider)) { + final addResult = await serviceProvider.addEmail( + eventProvider: eventProvider + ); + if (addResult != null && addResult) { Navigator.pop(context); ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Email was added'), ), ); - } else { + } else if(addResult != null && !addResult) { Navigator.pop(context); ScaffoldMessenger.of(context).showSnackBar( const SnackBar( @@ -43,6 +45,15 @@ class _AddEmailModalState extends State { ), ); } + else + { + Navigator.pop(context); + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Not added - User email already added'), + ), + ); + } }, child: const SizedBox( height: 30, diff --git a/client/lib/widgets/event_editing_widget.dart b/client/lib/widgets/event_editing_widget.dart index 09240425..4367a481 100644 --- a/client/lib/widgets/event_editing_widget.dart +++ b/client/lib/widgets/event_editing_widget.dart @@ -582,7 +582,7 @@ class EventEditingState extends State { final date = await showDatePicker( context: context, initialDate: initialDate, - firstDate: DateTime(initialDate.year - 5), + firstDate: DateTime.now(), lastDate: DateTime(initialDate.year + 10), ); diff --git a/client/lib/widgets/tasks_widget.dart b/client/lib/widgets/tasks_widget.dart index 26f23e16..d4b057e3 100644 --- a/client/lib/widgets/tasks_widget.dart +++ b/client/lib/widgets/tasks_widget.dart @@ -63,6 +63,7 @@ class TasksWidgetState extends State { 'assets/animations/empty.json', height: 200, width: 300, + repeat: false, ), ), ], diff --git a/client/pubspec.lock b/client/pubspec.lock index c429c7e5..2643bd8f 100644 --- a/client/pubspec.lock +++ b/client/pubspec.lock @@ -81,46 +81,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.4.1" - build_config: - dependency: transitive - description: - name: build_config - sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1 - url: "https://pub.dev" - source: hosted - version: "1.1.1" - build_daemon: - dependency: transitive - description: - name: build_daemon - sha256: "5f02d73eb2ba16483e693f80bee4f088563a820e47d1027d4cdfe62b5bb43e65" - url: "https://pub.dev" - source: hosted - version: "4.0.0" - build_resolvers: - dependency: transitive - description: - name: build_resolvers - sha256: "6c4dd11d05d056e76320b828a1db0fc01ccd376922526f8e9d6c796a5adbac20" - url: "https://pub.dev" - source: hosted - version: "2.2.1" - build_runner: - dependency: "direct dev" - description: - name: build_runner - sha256: "10c6bcdbf9d049a0b666702cf1cee4ddfdc38f02a19d35ae392863b47519848b" - url: "https://pub.dev" - source: hosted - version: "2.4.6" - build_runner_core: - dependency: transitive - description: - name: build_runner_core - sha256: "6d6ee4276b1c5f34f21fdf39425202712d2be82019983d52f351c94aafbc2c41" - url: "https://pub.dev" - source: hosted - version: "7.2.10" built_collection: dependency: transitive description: @@ -133,10 +93,10 @@ packages: dependency: transitive description: name: built_value - sha256: "598a2a682e2a7a90f08ba39c0aaa9374c5112340f0a2e275f61b59389543d166" + sha256: a8de5955205b4d1dbbbc267daddf2178bd737e4bab8987c04a500478c9651e74 url: "https://pub.dev" source: hosted - version: "8.6.1" + version: "8.6.3" characters: dependency: transitive description: @@ -153,6 +113,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.3" + cli_util: + dependency: transitive + description: + name: cli_util + sha256: b8db3080e59b2503ca9e7922c3df2072cf13992354d5e944074ffa836fba43b7 + url: "https://pub.dev" + source: hosted + version: "0.4.0" clock: dependency: transitive description: @@ -165,10 +133,10 @@ packages: dependency: transitive description: name: code_builder - sha256: "4ad01d6e56db961d29661561effde45e519939fdaeb46c351275b182eac70189" + sha256: "1be9be30396d7e4c0db42c35ea6ccd7cc6a1e19916b5dc64d6ac216b5544d677" url: "https://pub.dev" source: hosted - version: "4.5.0" + version: "4.7.0" collection: dependency: transitive description: @@ -355,14 +323,14 @@ packages: url: "https://pub.dev" source: hosted version: "5.7.2+3" - flutter_lints: - dependency: "direct dev" + flutter_launcher_icons: + dependency: "direct main" description: - name: flutter_lints - sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c + name: flutter_launcher_icons + sha256: "526faf84284b86a4cb36d20a5e45147747b7563d921373d4ee0559c54fcdbcea" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "0.13.1" flutter_pdfview: dependency: transitive description: @@ -413,14 +381,6 @@ packages: description: flutter source: sdk version: "0.0.0" - frontend_server_client: - dependency: transitive - description: - name: frontend_server_client - sha256: "408e3ca148b31c20282ad6f37ebfa6f4bdc8fede5b74bc2f08d9d92b55db3612" - url: "https://pub.dev" - source: hosted - version: "3.2.0" fuchsia_remote_debug_protocol: dependency: transitive description: flutter @@ -578,14 +538,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.4.1" - graphs: - dependency: transitive - description: - name: graphs - sha256: aedc5a15e78fc65a6e23bcd927f24c64dd995062bcd1ca6eda65a3cff92a4d19 - url: "https://pub.dev" - source: hosted - version: "2.3.1" http: dependency: "direct main" description: @@ -594,14 +546,6 @@ packages: url: "https://pub.dev" source: hosted version: "0.13.6" - http_multi_server: - dependency: transitive - description: - name: http_multi_server - sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b" - url: "https://pub.dev" - source: hosted - version: "3.2.1" http_parser: dependency: transitive description: @@ -618,6 +562,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.0" + image: + dependency: transitive + description: + name: image + sha256: a72242c9a0ffb65d03de1b7113bc4e189686fc07c7147b8b41811d0dd0e0d9bf + url: "https://pub.dev" + source: hosted + version: "4.0.17" infinite_listview: dependency: transitive description: @@ -639,14 +591,6 @@ packages: url: "https://pub.dev" source: hosted version: "0.18.1" - io: - dependency: transitive - description: - name: io - sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" - url: "https://pub.dev" - source: hosted - version: "1.0.4" js: dependency: transitive description: @@ -679,14 +623,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.1" - lints: - dependency: transitive - description: - name: lints - sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" - url: "https://pub.dev" - source: hosted - version: "2.1.1" logging: dependency: transitive description: @@ -727,16 +663,8 @@ packages: url: "https://pub.dev" source: hosted version: "1.9.1" - mime: - dependency: transitive - description: - name: mime - sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e - url: "https://pub.dev" - source: hosted - version: "1.0.4" mockito: - dependency: "direct dev" + dependency: "direct main" description: name: mockito sha256: "7d5b53bcd556c1bc7ffbe4e4d5a19c3e112b7e925e9e172dd7c6ad0630812616" @@ -903,14 +831,6 @@ packages: url: "https://pub.dev" source: hosted version: "3.7.3" - pool: - dependency: transitive - description: - name: pool - sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" - url: "https://pub.dev" - source: hosted - version: "1.5.1" process: dependency: transitive description: @@ -959,14 +879,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.0" - pubspec_parse: - dependency: transitive - description: - name: pubspec_parse - sha256: c63b2876e58e194e4b0828fcb080ad0e06d051cb607a6be51a9e084f47cb9367 - url: "https://pub.dev" - source: hosted - version: "1.2.3" quiver: dependency: transitive description: @@ -983,22 +895,6 @@ packages: url: "https://pub.dev" source: hosted version: "0.27.7" - shelf: - dependency: transitive - description: - name: shelf - sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4 - url: "https://pub.dev" - source: hosted - version: "1.4.1" - shelf_web_socket: - dependency: transitive - description: - name: shelf_web_socket - sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1" - url: "https://pub.dev" - source: hosted - version: "1.0.4" sky_engine: dependency: transitive description: flutter @@ -1052,14 +948,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.1" - stream_transform: - dependency: transitive - description: - name: stream_transform - sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f" - url: "https://pub.dev" - source: hosted - version: "2.1.0" string_scanner: dependency: transitive description: @@ -1148,14 +1036,6 @@ packages: url: "https://pub.dev" source: hosted version: "0.9.2" - timing: - dependency: transitive - description: - name: timing - sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32" - url: "https://pub.dev" - source: hosted - version: "1.0.1" typed_data: dependency: transitive description: @@ -1332,14 +1212,6 @@ packages: url: "https://pub.dev" source: hosted version: "0.1.4-beta" - web_socket_channel: - dependency: transitive - description: - name: web_socket_channel - sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b - url: "https://pub.dev" - source: hosted - version: "2.4.0" webdriver: dependency: transitive description: diff --git a/client/pubspec.yaml b/client/pubspec.yaml index 401642c2..5f9c29f1 100644 --- a/client/pubspec.yaml +++ b/client/pubspec.yaml @@ -74,6 +74,9 @@ dependencies: # flutter_pdfview: ^1.3.1 pdfx: ^2.4.0 flutter_cached_pdfview: ^0.4.2 + flutter_launcher_icons: ^0.13.1 + mockito: ^5.4.2 + dev_dependencies: flutter_test: @@ -82,6 +85,15 @@ dev_dependencies: integration_test: sdk: flutter + +flutter_icons: + android: true + ios: true + remove_alpha_ios: true + image_path: "assets/icons/koja_foreground.png" + adaptive_icon_foreground: "assets/icons/koja_foreground.png" + adaptive_icon_background: "#6B1DE2" + # The "flutter_lints" package below contains a set of recommended lints to # encourage good coding practices. The lint set provided by the package is # activated in the `analysis_options.yaml` file located at the root of your diff --git a/src/main/kotlin/com/teamcaffeine/koja/constants/ResponseConstant.kt b/src/main/kotlin/com/teamcaffeine/koja/constants/ResponseConstant.kt index ce4987e7..c13d1938 100644 --- a/src/main/kotlin/com/teamcaffeine/koja/constants/ResponseConstant.kt +++ b/src/main/kotlin/com/teamcaffeine/koja/constants/ResponseConstant.kt @@ -8,9 +8,14 @@ object ResponseConstant { const val EVENT_CREATION_FAILED_INTERNAL_ERROR = "Internal Server Error - Event could no be created." const val EVENT_UPDATE_FAILED_INTERNAL_ERROR = "Internal Server Error - Event could not be updated." const val EVENT_DELETION_FAILED_INTERNAL_ERROR = "Internal Server Error - Event could not be deleted." + const val SET_USER_CALENDAR_FAILED_INTERNAL_ERROR = "Internal Server Error - User calendar could not be set." const val INVALID_PARAMETERS = "Provided parameters invalid." const val GENERIC_INTERNAL_ERROR = "Internal Server Error - Something went wrong." const val UNAUTHORIZED = "Unauthorized request." + const val INVALID_TOKEN = "Invalid token." + const val NO_FUTURE_LOCATIONS_FOUND = "No future locations found." + const val SET_TIME_BOUNDARY_FAILED_INTERNAL_ERROR = "Internal Server Error - Time boundary could not be set." + const val USER_ALREADY_EXISTS = "User already exists." // Response messages where system succeeded const val EVENT_CREATED = "Event successfully created." @@ -20,4 +25,7 @@ object ResponseConstant { const val ACCOUNT_DELETED = "Account successfully deleted." const val HOME_LOCATION_SET = "Home location successfully set." const val WORK_LOCATION_SET = "Work location successfully set." + const val USER_CALENDAR_SET = "User calendar successfully set." + const val SUCCESSFULLY_ADDED_TIME_BOUNDARY = "Successfully added time boundary." + const val SUCCESSFULLY_REMOVED_TIME_BOUNDARY = "Successfully removed time boundary." } diff --git a/src/main/kotlin/com/teamcaffeine/koja/controller/AIDataController.kt b/src/main/kotlin/com/teamcaffeine/koja/controller/AIDataController.kt index cdf28a76..5ca1aa8e 100644 --- a/src/main/kotlin/com/teamcaffeine/koja/controller/AIDataController.kt +++ b/src/main/kotlin/com/teamcaffeine/koja/controller/AIDataController.kt @@ -12,6 +12,7 @@ import com.teamcaffeine.koja.dto.AIRequestBodyDTO import com.teamcaffeine.koja.service.AIUserDataService import com.teamcaffeine.koja.service.CryptoService import com.teamcaffeine.koja.service.UserCalendarService +import org.springframework.http.HttpStatus import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.RequestHeader @@ -27,42 +28,54 @@ class AIDataController(private val aiUserDataService: AIUserDataService, private @GetMapping("/get-emails") fun getUserEmail(@RequestHeader(HeaderConstant.AUTHORISATION) token: String?): ResponseEntity { - return if (token == null) { - ResponseEntity.badRequest().body(ResponseConstant.REQUIRED_PARAMETERS_NOT_SET) - } else { - val returnableMap = mapOf( - "502" to "u19012366@tuks.co.za", - "552" to "u21566382@tuks.co.za", - "553" to "lesibasetsiba02@gmail.com", - "602" to "u20505656@tuks.co.za", - "603" to "uunarineleo@gmail.com", - "652" to "u21609633@tuks.co.za", - ) - val gson = Gson() - val jsonObject = gson.toJson(returnableMap) - ResponseEntity.ok(jsonObject) + return when { + token == null -> { + ResponseEntity.badRequest().body(ResponseConstant.REQUIRED_PARAMETERS_NOT_SET) + } + !TokenManagerController.isTokenValid(token) -> { + ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(ResponseConstant.INVALID_TOKEN) + } + else -> { + val returnableMap = mapOf( + "502" to "u19012366@tuks.co.za", + "552" to "u21566382@tuks.co.za", + "553" to "lesibasetsiba02@gmail.com", + "602" to "u20505656@tuks.co.za", + "603" to "uunarineleo@gmail.com", + "652" to "u21609633@tuks.co.za", + ) + val gson = Gson() + val jsonObject = gson.toJson(returnableMap) + ResponseEntity.ok(jsonObject) + } } } @GetMapping("/get-user-events") fun getUsersSuggestions(@RequestHeader(HeaderConstant.AUTHORISATION) token: String?, @RequestParam("userID") userID: String?): ResponseEntity { - return if (token == null || userID == null) { - ResponseEntity.badRequest().body(ResponseConstant.REQUIRED_PARAMETERS_NOT_SET) - } else { - val gsonBuilder = GsonBuilder() - gsonBuilder.registerTypeAdapter( - OffsetDateTime::class.java, - OffsetDateTimeAdapter(), - ) - gsonBuilder.registerTypeAdapter(OffsetDateTime::class.java, OffsetDateTimeAdapter()) - val gson: Gson = gsonBuilder.setPrettyPrinting().create() - var userIDRequest = userID - if (userIDRequest.toIntOrNull() == null) { - userIDRequest = TokenManagerController.getUserJWTTokenData(token).userID.toString() + return when { + token == null || userID == null -> { + ResponseEntity.badRequest().body(ResponseConstant.REQUIRED_PARAMETERS_NOT_SET) + } + !TokenManagerController.isTokenValid(token) -> { + ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(ResponseConstant.INVALID_TOKEN) + } + else -> { + val gsonBuilder = GsonBuilder() + gsonBuilder.registerTypeAdapter( + OffsetDateTime::class.java, + OffsetDateTimeAdapter(), + ) + gsonBuilder.registerTypeAdapter(OffsetDateTime::class.java, OffsetDateTimeAdapter()) + val gson: Gson = gsonBuilder.setPrettyPrinting().create() + var userIDRequest = userID + if (userIDRequest.toIntOrNull() == null) { + userIDRequest = TokenManagerController.getUserJWTTokenData(token).userID.toString() + } + val userEvents = userCalendarService.getUserSuggestions(userIDRequest) + val jsonObject = gson.toJson(userEvents) + ResponseEntity.ok(jsonObject) } - val userEvents = userCalendarService.getUserSuggestions(userIDRequest) - val jsonObject = gson.toJson(userEvents) - ResponseEntity.ok(jsonObject) } } diff --git a/src/main/kotlin/com/teamcaffeine/koja/controller/CalendarController.kt b/src/main/kotlin/com/teamcaffeine/koja/controller/CalendarController.kt index fe2477d5..54ed9a31 100644 --- a/src/main/kotlin/com/teamcaffeine/koja/controller/CalendarController.kt +++ b/src/main/kotlin/com/teamcaffeine/koja/controller/CalendarController.kt @@ -25,28 +25,40 @@ class CalendarController(private val userCalendar: UserCalendarService) { @RequestHeader(HeaderConstant.AUTHORISATION) token: String?, @RequestBody event: UserEventDTO?, ): ResponseEntity { - return if (token == null || event == null) { - ResponseEntity.badRequest().body(ResponseConstant.REQUIRED_PARAMETERS_NOT_SET) - } else { - try { - userCalendar.createEvent(token, event) - } catch (e: Exception) { - if (e.message.equals(ExceptionMessageConstant.UNABLE_TO_FIT_EVENT)) { - ResponseEntity.badRequest() - .body(ResponseConstant.EVENT_CREATION_FAILED_COULD_NOT_FIT) + return when { + token == null || event == null -> { + ResponseEntity.badRequest().body(ResponseConstant.REQUIRED_PARAMETERS_NOT_SET) + } + !TokenManagerController.isTokenValid(token) -> { + ResponseEntity.badRequest().body(ResponseConstant.INVALID_TOKEN) + } + else -> { + try { + userCalendar.createEvent(token, event) + } catch (e: Exception) { + if (e.message.equals(ExceptionMessageConstant.UNABLE_TO_FIT_EVENT)) { + ResponseEntity.badRequest() + .body(ResponseConstant.EVENT_CREATION_FAILED_COULD_NOT_FIT) + } + return ResponseEntity.internalServerError().body(e.message) } - return ResponseEntity.internalServerError().body(e.message) + ResponseEntity.ok(ResponseConstant.EVENT_CREATED) } - ResponseEntity.ok(ResponseConstant.EVENT_CREATED) } } @GetMapping("/userEvents") fun getAllUserEvents(@RequestHeader(HeaderConstant.AUTHORISATION) token: String?): ResponseEntity { - return if (token == null) { - ResponseEntity.badRequest().body(ResponseConstant.REQUIRED_PARAMETERS_NOT_SET) - } else { - ResponseEntity.ok(userCalendar.getAllUserEvents(token)) + return when { + token == null -> { + ResponseEntity.badRequest().body(ResponseConstant.REQUIRED_PARAMETERS_NOT_SET) + } + !TokenManagerController.isTokenValid(token) -> { + ResponseEntity.badRequest().body(ResponseConstant.INVALID_TOKEN) + } + else -> { + ResponseEntity.ok(userCalendar.getAllUserEvents(token)) + } } } @@ -55,17 +67,23 @@ class CalendarController(private val userCalendar: UserCalendarService) { @RequestHeader(HeaderConstant.AUTHORISATION) token: String?, @RequestBody updatedEvent: UserEventDTO?, ): ResponseEntity { - return if (token == null || updatedEvent == null) { - return ResponseEntity.badRequest().body(ResponseConstant.REQUIRED_PARAMETERS_NOT_SET) - } else { - try { - deleteEvent(token, updatedEvent) - addEvent(token, updatedEvent) -// userCalendar.updateEvent(token, updatedEvent) - } catch (e: Exception) { - ResponseEntity.badRequest().body(ResponseConstant.EVENT_UPDATE_FAILED_INTERNAL_ERROR) - } - ResponseEntity.ok(ResponseConstant.EVENT_UPDATED) + return when { + token == null || updatedEvent == null -> { + ResponseEntity.badRequest().body(ResponseConstant.REQUIRED_PARAMETERS_NOT_SET) + } + !TokenManagerController.isTokenValid(token) -> { + ResponseEntity.badRequest().body(ResponseConstant.INVALID_TOKEN) + } + else -> { + try { + deleteEvent(token, updatedEvent) + addEvent(token, updatedEvent) +// userCalendar.updateEvent(token, updatedEvent) + } catch (e: Exception) { + ResponseEntity.badRequest().body(ResponseConstant.EVENT_UPDATE_FAILED_INTERNAL_ERROR) + } + ResponseEntity.ok(ResponseConstant.EVENT_UPDATED) + } } } @@ -74,17 +92,22 @@ class CalendarController(private val userCalendar: UserCalendarService) { @RequestHeader(HeaderConstant.AUTHORISATION) token: String?, @RequestBody event: UserEventDTO?, ): ResponseEntity { - if (event == null || token == null) { - return ResponseEntity.badRequest().body(ResponseConstant.REQUIRED_PARAMETERS_NOT_SET) - } - - try { - userCalendar.deleteEvent(token, event.getId()) - } catch (e: Exception) { - return ResponseEntity.badRequest().body(ResponseConstant.EVENT_DELETION_FAILED_INTERNAL_ERROR) + return when { + token == null || event == null -> { + ResponseEntity.badRequest().body(ResponseConstant.REQUIRED_PARAMETERS_NOT_SET) + } + !TokenManagerController.isTokenValid(token) -> { + ResponseEntity.badRequest().body(ResponseConstant.INVALID_TOKEN) + } + else -> { + try { + userCalendar.deleteEvent(token, event.getId()) + } catch (e: Exception) { + ResponseEntity.badRequest().body(ResponseConstant.EVENT_DELETION_FAILED_INTERNAL_ERROR) + } + ResponseEntity.ok(ResponseConstant.EVENT_DELETED) + } } - - return ResponseEntity.ok(ResponseConstant.EVENT_DELETED) } @PostMapping("/rescheduleEvent") @@ -92,25 +115,32 @@ class CalendarController(private val userCalendar: UserCalendarService) { @RequestHeader(HeaderConstant.AUTHORISATION) token: String?, @RequestBody event: UserEventDTO?, ): ResponseEntity { - if (event == null || token == null) { - return ResponseEntity.badRequest().body(ResponseConstant.REQUIRED_PARAMETERS_NOT_SET) - } - try { - // val timeZoneId = ZoneId.of(TimezoneUtility(userRepository, googleCalendarAdapterService).getTimeOfTimeZone(token)) - val timeZoneId = ZoneId.of("Africa/Johannesburg") - val currentTime = OffsetDateTime.now(timeZoneId) - userCalendar.deleteEvent(token, event.getId()) - event.setStartTime(currentTime) - event.setEndTime(currentTime.plusSeconds(event.getDurationInSeconds())) - val events = userCalendar.getAllUserEvents(token) - if (checkAvailability(events)) { - userCalendar.createEvent(token, event) - return ResponseEntity.ok(ResponseConstant.EVENT_UPDATED) - } - } catch (e: Exception) { - return ResponseEntity.badRequest().body(e.stackTraceToString()) + return when { + token == null || event == null -> { + ResponseEntity.badRequest().body(ResponseConstant.REQUIRED_PARAMETERS_NOT_SET) + } + !TokenManagerController.isTokenValid(token) -> { + ResponseEntity.badRequest().body(ResponseConstant.INVALID_TOKEN) + } + else -> { + try { + // val timeZoneId = ZoneId.of(TimezoneUtility(userRepository, googleCalendarAdapterService).getTimeOfTimeZone(token)) + val timeZoneId = ZoneId.of("Africa/Johannesburg") + val currentTime = OffsetDateTime.now(timeZoneId) + userCalendar.deleteEvent(token, event.getId()) + event.setStartTime(currentTime) + event.setEndTime(currentTime.plusSeconds(event.getDurationInSeconds())) + val events = userCalendar.getAllUserEvents(token) + if (checkAvailability(events)) { + userCalendar.createEvent(token, event) + return ResponseEntity.ok(ResponseConstant.EVENT_UPDATED) + } + } catch (e: Exception) { + ResponseEntity.badRequest().body(ResponseConstant.EVENT_UPDATE_FAILED_INTERNAL_ERROR) + } + ResponseEntity.ok(ResponseConstant.EVENT_UPDATED) + } } - return ResponseEntity.badRequest().body(ResponseConstant.EVENT_UPDATE_FAILED_INTERNAL_ERROR) } @PostMapping("/rescheduleTimeSlot") @@ -118,24 +148,31 @@ class CalendarController(private val userCalendar: UserCalendarService) { @RequestHeader(HeaderConstant.AUTHORISATION) token: String?, @RequestBody event: UserEventDTO?, ): ResponseEntity { - if (event == null || token == null) { - return ResponseEntity.badRequest().body(ResponseConstant.REQUIRED_PARAMETERS_NOT_SET) - } - try { - // val timeZoneId = ZoneId.of(TimezoneUtility(userRepository, googleCalendarAdapterService).getTimeOfTimeZone(token)) - val timeZoneId = ZoneId.of("Africa/Johannesburg") - val currentTime = OffsetDateTime.now(timeZoneId) - userCalendar.deleteEvent(token, event.getId()) - val events = userCalendar.getAllUserEvents(token) - val timeslot = userCalendar.findEarliestTimeSlot(events, event) - event.setStartTime(timeslot.first) - event.setEndTime(timeslot.second) - userCalendar.createEvent(token, event) - return ResponseEntity.ok(ResponseConstant.EVENT_UPDATED) - } catch (e: Exception) { - return ResponseEntity.badRequest().body(e.stackTraceToString()) + return when { + event == null || token == null -> { + ResponseEntity.badRequest().body(ResponseConstant.REQUIRED_PARAMETERS_NOT_SET) + } + !TokenManagerController.isTokenValid(token) -> { + ResponseEntity.badRequest().body(ResponseConstant.INVALID_TOKEN) + } + else -> { + try { + // val timeZoneId = ZoneId.of(TimezoneUtility(userRepository, googleCalendarAdapterService).getTimeOfTimeZone(token)) + val timeZoneId = ZoneId.of("Africa/Johannesburg") + val currentTime = OffsetDateTime.now(timeZoneId) + userCalendar.deleteEvent(token, event.getId()) + val events = userCalendar.getAllUserEvents(token) + val timeslot = userCalendar.findEarliestTimeSlot(events, event) + event.setStartTime(timeslot.first) + event.setEndTime(timeslot.second) + userCalendar.createEvent(token, event) + ResponseEntity.ok(ResponseConstant.EVENT_UPDATED) + } catch (e: Exception) { + ResponseEntity.badRequest().body(ResponseConstant.EVENT_UPDATE_FAILED_INTERNAL_ERROR) + } + ResponseEntity.ok(ResponseConstant.EVENT_UPDATED) + } } - return ResponseEntity.badRequest().body(ResponseConstant.EVENT_UPDATE_FAILED_INTERNAL_ERROR) } fun checkAvailability(events: List): Boolean { @@ -150,26 +187,38 @@ class CalendarController(private val userCalendar: UserCalendarService) { @PostMapping("/setSuggestedCalendar") fun setSuggestedCalendar( @RequestHeader(HeaderConstant.AUTHORISATION) token: String?, - @RequestBody eventList: List, + @RequestBody eventList: List?, ): ResponseEntity { - if (eventList == null || token == null) { - return ResponseEntity.badRequest().body(ResponseConstant.REQUIRED_PARAMETERS_NOT_SET) - } else { - try { - userCalendar.createNewCalendar(token, eventList) - } catch (e: Exception) { - return ResponseEntity.badRequest().body(e.printStackTrace()) - } - return ResponseEntity.ok("New calendar successfully created.") + return when { + eventList == null || token == null -> { + ResponseEntity.badRequest().body(ResponseConstant.REQUIRED_PARAMETERS_NOT_SET) + } + !TokenManagerController.isTokenValid(token) -> { + ResponseEntity.badRequest().body(ResponseConstant.INVALID_TOKEN) + } + else -> { + try { + userCalendar.createNewCalendar(token, eventList) + } catch (e: Exception) { + return ResponseEntity.badRequest().body(ResponseConstant.SET_USER_CALENDAR_FAILED_INTERNAL_ERROR) + } + ResponseEntity.ok(ResponseConstant.USER_CALENDAR_SET) + } } } @GetMapping("/userEventsKojaSuggestions") fun getAllUserEventsKojaSuggestions(@RequestHeader(HeaderConstant.AUTHORISATION) token: String?): ResponseEntity { - return if (token == null) { - ResponseEntity.badRequest().body(ResponseConstant.REQUIRED_PARAMETERS_NOT_SET) - } else { - ResponseEntity.ok(userCalendar.getAllUserEventsKojaSuggestions(token)) + return when { + token == null -> { + ResponseEntity.badRequest().body(ResponseConstant.REQUIRED_PARAMETERS_NOT_SET) + } + !TokenManagerController.isTokenValid(token) -> { + ResponseEntity.badRequest().body(ResponseConstant.INVALID_TOKEN) + } + else -> { + ResponseEntity.ok(userCalendar.getAllUserEventsKojaSuggestions(token)) + } } } } diff --git a/src/main/kotlin/com/teamcaffeine/koja/controller/LocationController.kt b/src/main/kotlin/com/teamcaffeine/koja/controller/LocationController.kt index a147afce..d9f2d570 100644 --- a/src/main/kotlin/com/teamcaffeine/koja/controller/LocationController.kt +++ b/src/main/kotlin/com/teamcaffeine/koja/controller/LocationController.kt @@ -43,33 +43,45 @@ LocationController(private val locationService: LocationService) { @PostMapping("/HomeLocationUpdater") fun updateUserHomeLocation(@RequestHeader(HeaderConstant.AUTHORISATION) token: String?, @RequestParam("placeId") placeId: String?): ResponseEntity { - return if (token == null) { - ResponseEntity.badRequest().body(ResponseConstant.REQUIRED_PARAMETERS_NOT_SET) - } else { - try { - locationService.setHomeLocation(token, placeId) - } catch (e: IllegalArgumentException) { - ResponseEntity.badRequest().body(ResponseConstant.INVALID_PARAMETERS) - } catch (e: Exception) { - ResponseEntity.badRequest().body(ResponseConstant.GENERIC_INTERNAL_ERROR) + return when { + token == null || placeId == null -> { + ResponseEntity.badRequest().body(ResponseConstant.REQUIRED_PARAMETERS_NOT_SET) + } + !TokenManagerController.isTokenValid(token) -> { + ResponseEntity.badRequest().body(ResponseConstant.INVALID_TOKEN) + } + else -> { + try { + locationService.setHomeLocation(token, placeId) + } catch (e: IllegalArgumentException) { + ResponseEntity.badRequest().body(ResponseConstant.INVALID_PARAMETERS) + } catch (e: Exception) { + ResponseEntity.badRequest().body(ResponseConstant.GENERIC_INTERNAL_ERROR) + } + ResponseEntity.ok(ResponseConstant.HOME_LOCATION_SET) } - ResponseEntity.ok(ResponseConstant.HOME_LOCATION_SET) } } @PostMapping("/WorkLocationUpdater") fun updateUserWorkLocation(@RequestHeader(HeaderConstant.AUTHORISATION) token: String?, @RequestParam("placeId") placeId: String?): ResponseEntity { - return if (token == null) { - ResponseEntity.badRequest().body(ResponseConstant.REQUIRED_PARAMETERS_NOT_SET) - } else { - try { - locationService.setWorkLocation(token, placeId) - } catch (e: IllegalArgumentException) { - ResponseEntity.badRequest().body(ResponseConstant.INVALID_PARAMETERS) - } catch (e: Exception) { - ResponseEntity.badRequest().body(ResponseConstant.GENERIC_INTERNAL_ERROR) + return when { + token == null || placeId == null -> { + ResponseEntity.badRequest().body(ResponseConstant.REQUIRED_PARAMETERS_NOT_SET) + } + !TokenManagerController.isTokenValid(token) -> { + ResponseEntity.badRequest().body(ResponseConstant.INVALID_TOKEN) + } + else -> { + try { + locationService.setWorkLocation(token, placeId) + } catch (e: IllegalArgumentException) { + ResponseEntity.badRequest().body(ResponseConstant.INVALID_PARAMETERS) + } catch (e: Exception) { + ResponseEntity.badRequest().body(ResponseConstant.GENERIC_INTERNAL_ERROR) + } + ResponseEntity.ok(ResponseConstant.WORK_LOCATION_SET) } - ResponseEntity.ok(ResponseConstant.WORK_LOCATION_SET) } } @@ -79,12 +91,24 @@ LocationController(private val locationService: LocationService) { @RequestParam("origin") origin: String?, @RequestParam("destination") destination: String?, ): ResponseEntity { - if (token == null || origin == null || destination == null || origin.isEmpty() || destination.isEmpty()) { - return ResponseEntity.badRequest().body(ResponseConstant.REQUIRED_PARAMETERS_NOT_SET) + return when { + token == null || origin.isNullOrEmpty() || destination.isNullOrEmpty() -> { + ResponseEntity.badRequest().body(ResponseConstant.REQUIRED_PARAMETERS_NOT_SET) + } + !TokenManagerController.isTokenValid(token) -> { + ResponseEntity.badRequest().body(ResponseConstant.INVALID_TOKEN) + } + else -> { + try { + val distance = getDistance(origin, destination) + ResponseEntity.ok(distance) + } catch (e: IllegalArgumentException) { + ResponseEntity.badRequest().body(ResponseConstant.INVALID_PARAMETERS) + } catch (e: Exception) { + ResponseEntity.badRequest().body(ResponseConstant.GENERIC_INTERNAL_ERROR) + } + } } - - val distance = getDistance(origin, destination) - return ResponseEntity.ok(distance) } @GetMapping("/listOfLocationTravelTime") @@ -93,17 +117,29 @@ LocationController(private val locationService: LocationService) { @RequestParam("originLat") originLat: String?, @RequestParam("originLng") originLng: String?, ): ResponseEntity { - return if (token == null || originLng == null || originLat == null) { - ResponseEntity.badRequest().body(ResponseConstant.REQUIRED_PARAMETERS_NOT_SET) - } else { - val originLatDouble = originLat.toDouble() - val originLngDouble = originLng.toDouble() - val travelTimeList = locationService.getLocationTravelTimes(token, originLatDouble, originLngDouble) + return when { + token == null || originLat.isNullOrEmpty() || originLng.isNullOrEmpty() -> { + ResponseEntity.badRequest().body(ResponseConstant.REQUIRED_PARAMETERS_NOT_SET) + } + !TokenManagerController.isTokenValid(token) -> { + ResponseEntity.badRequest().body(ResponseConstant.INVALID_TOKEN) + } + else -> { + try { + val originLatDouble = originLat.toDouble() + val originLngDouble = originLng.toDouble() + val travelTimeList = locationService.getLocationTravelTimes(token, originLatDouble, originLngDouble) - if (travelTimeList.isNotEmpty()) { - ResponseEntity.ok(travelTimeList) - } else { - ResponseEntity.ok("There are no future locations.") + if (travelTimeList.isNotEmpty()) { + ResponseEntity.ok(travelTimeList) + } else { + ResponseEntity.ok(ResponseConstant.NO_FUTURE_LOCATIONS_FOUND) + } + } catch (e: IllegalArgumentException) { + ResponseEntity.badRequest().body(ResponseConstant.INVALID_PARAMETERS) + } catch (e: Exception) { + ResponseEntity.badRequest().body(ResponseConstant.GENERIC_INTERNAL_ERROR) + } } } } @@ -129,18 +165,24 @@ LocationController(private val locationService: LocationService) { @RequestParam("destLat") destLat: String?, @RequestParam("destLng") destLng: String?, ): ResponseEntity { - return if (token == null || placeId == null || destLat == null || destLng == null) { - ResponseEntity.badRequest().body(ResponseConstant.REQUIRED_PARAMETERS_NOT_SET) - } else { - try { - val destLatDouble = destLat.toDouble() - val destLngDouble = destLng.toDouble() - val result = getTravelTime(placeId, destLatDouble, destLngDouble) - ResponseEntity.ok().body(result ?: 0L) - } catch (e: NumberFormatException) { - ResponseEntity.badRequest().body(ResponseConstant.INVALID_PARAMETERS) - } catch (e: Exception) { - ResponseEntity.badRequest().body(ResponseConstant.GENERIC_INTERNAL_ERROR) + return when { + token.isNullOrEmpty() || placeId.isNullOrEmpty() || destLat.isNullOrEmpty() || destLng.isNullOrEmpty() -> { + ResponseEntity.badRequest().body(ResponseConstant.REQUIRED_PARAMETERS_NOT_SET) + } + !TokenManagerController.isTokenValid(token) -> { + ResponseEntity.badRequest().body(ResponseConstant.INVALID_TOKEN) + } + else -> { + try { + val destLatDouble = destLat.toDouble() + val destLngDouble = destLng.toDouble() + val result = getTravelTime(placeId, destLatDouble, destLngDouble) + ResponseEntity.ok().body(result ?: 0L) + } catch (e: NumberFormatException) { + ResponseEntity.badRequest().body(ResponseConstant.INVALID_PARAMETERS) + } catch (e: Exception) { + ResponseEntity.badRequest().body(ResponseConstant.GENERIC_INTERNAL_ERROR) + } } } } @@ -162,30 +204,44 @@ LocationController(private val locationService: LocationService) { @PostMapping("/updateLocation") fun updateCurrentUserLocation( @RequestHeader(HeaderConstant.AUTHORISATION) token: String?, - @RequestParam("latitude") latitude: String, - @RequestParam("longitude") longitude: String, + @RequestParam("latitude") latitude: String?, + @RequestParam("longitude") longitude: String?, ): ResponseEntity { - return if (token == null) { - ResponseEntity.badRequest().body(ResponseConstant.REQUIRED_PARAMETERS_NOT_SET) - } else { - try { - val destLatDouble = latitude.toDouble() - val destLngDouble = longitude.toDouble() - ResponseEntity.ok(locationService.updateUserLocation(token, destLatDouble, destLngDouble)) - } catch (e: Exception) { - ResponseEntity.badRequest().body(ResponseConstant.INVALID_PARAMETERS) - } catch (e: Exception) { - ResponseEntity.badRequest().body(ResponseConstant.GENERIC_INTERNAL_ERROR) + return when { + token.isNullOrEmpty() || latitude.isNullOrEmpty() || longitude.isNullOrEmpty() -> { + ResponseEntity.badRequest().body(ResponseConstant.REQUIRED_PARAMETERS_NOT_SET) + } + !TokenManagerController.isTokenValid(token) -> { + ResponseEntity.badRequest().body(ResponseConstant.INVALID_TOKEN) + } + else -> { + try { + val destLatDouble = latitude.toDouble() + val destLngDouble = longitude.toDouble() + ResponseEntity.ok(locationService.updateUserLocation(token, destLatDouble, destLngDouble)) + } catch (e: Exception) { + ResponseEntity.badRequest().body(ResponseConstant.GENERIC_INTERNAL_ERROR) + } } } } @GetMapping("/savedLocations") fun getUserSavedLocations(@RequestHeader(HeaderConstant.AUTHORISATION) token: String?): ResponseEntity { - return if (token == null) { - ResponseEntity.badRequest().body(ResponseConstant.REQUIRED_PARAMETERS_NOT_SET) - } else { - ResponseEntity.ok(locationService.getUserSavedLocations(token)) + return when { + token == null -> { + ResponseEntity.badRequest().body(ResponseConstant.REQUIRED_PARAMETERS_NOT_SET) + } + !TokenManagerController.isTokenValid(token) -> { + ResponseEntity.badRequest().body(ResponseConstant.INVALID_TOKEN) + } + else -> { + try { + ResponseEntity.ok(locationService.getUserSavedLocations(token)) + } catch (e: Exception) { + ResponseEntity.badRequest().body(ResponseConstant.GENERIC_INTERNAL_ERROR) + } + } } } } diff --git a/src/main/kotlin/com/teamcaffeine/koja/controller/TokenManagerController.kt b/src/main/kotlin/com/teamcaffeine/koja/controller/TokenManagerController.kt index e027113a..df118211 100644 --- a/src/main/kotlin/com/teamcaffeine/koja/controller/TokenManagerController.kt +++ b/src/main/kotlin/com/teamcaffeine/koja/controller/TokenManagerController.kt @@ -5,6 +5,7 @@ import com.auth0.jwt.algorithms.Algorithm import com.google.gson.Gson import com.teamcaffeine.koja.constants.EnvironmentVariableConstant import com.teamcaffeine.koja.constants.HeaderConstant +import com.teamcaffeine.koja.constants.ResponseConstant import com.teamcaffeine.koja.dto.JWTAuthDetailsDTO import com.teamcaffeine.koja.dto.JWTAuthDetailsDTO.Companion.parseJWTFormatString import com.teamcaffeine.koja.dto.JWTGoogleDTO @@ -31,20 +32,26 @@ class TokenManagerController { @PostMapping("/renew") fun renewToken(@RequestHeader(HeaderConstant.AUTHORISATION) token: String?): Any { - return if (token == null) { - ResponseEntity.badRequest().body("ResponseConstant.REQUIRED_PARAMETERS_NOT_SET") - } else { - val jwtToken = getUserJWTTokenData(token, false) - for (t in jwtToken.userAuthDetails) { - t.renewToken() + return when { + token.isNullOrEmpty() -> { + ResponseEntity.badRequest().body(ResponseConstant.REQUIRED_PARAMETERS_NOT_SET) + } + isTokenValid(token) -> { + ResponseEntity.badRequest().body(ResponseConstant.INVALID_TOKEN) + } + else -> { + val jwtToken = getUserJWTTokenData(token, false) + for (t in jwtToken.userAuthDetails) { + t.renewToken() + } + val tokenRequest = TokenRequest( + tokens = jwtToken.userAuthDetails, + authProvider = AuthProviderEnum.NONE, + userId = jwtToken.userID, + ) + val newToken = createToken(tokenRequest) + ResponseEntity.ok(newToken) } - val tokenRequest = TokenRequest( - tokens = jwtToken.userAuthDetails, - authProvider = AuthProviderEnum.NONE, - userId = jwtToken.userID, - ) - val newToken = createToken(tokenRequest) - ResponseEntity.ok(newToken) } } @@ -60,6 +67,18 @@ class TokenManagerController { return String(decrypted, Charsets.UTF_8) } + fun isTokenValid(token: String): Boolean { + return try { + val jwtSecret: String = System.getProperty(EnvironmentVariableConstant.KOJA_JWT_SECRET) + val algorithm = Algorithm.HMAC512(jwtSecret) + val verifier = JWT.require(algorithm).build() + verifier.verify(token) + true + } catch (exception: Exception) { + false + } + } + fun createToken(tokenRequest: TokenRequest): String { val soonestExpireTime = getSoonestExpiryTime(tokenRequest) val expiryTime = soonestExpireTime - (oneMinuteInSeconds * 5) diff --git a/src/main/kotlin/com/teamcaffeine/koja/controller/UserAccountManagerController.kt b/src/main/kotlin/com/teamcaffeine/koja/controller/UserAccountManagerController.kt index eef22553..f50117c1 100644 --- a/src/main/kotlin/com/teamcaffeine/koja/controller/UserAccountManagerController.kt +++ b/src/main/kotlin/com/teamcaffeine/koja/controller/UserAccountManagerController.kt @@ -25,10 +25,21 @@ class UserAccountManagerController( @GetMapping("auth/add-email/google") fun authenticateAnotherGoogleEmail(request: HttpServletRequest, @RequestParam token: String?): Any { - return if (token == null) { - ResponseEntity.badRequest().body(ResponseConstant.REQUIRED_PARAMETERS_NOT_SET) - } else { - return googleCalendarAdapter.setupConnection(request, CallbackConfigEnum.ADD_EMAIL, addAdditionalAccount = true, token = token) + return when { + token.isNullOrEmpty() -> { + ResponseEntity.badRequest().body(ResponseConstant.REQUIRED_PARAMETERS_NOT_SET) + } + !TokenManagerController.isTokenValid(token) -> { + ResponseEntity.badRequest().body(ResponseConstant.INVALID_TOKEN) + } + else -> { + googleCalendarAdapter.setupConnection( + request, + CallbackConfigEnum.ADD_EMAIL, + addAdditionalAccount = true, + token = token, + ) + } } } @@ -37,11 +48,21 @@ class UserAccountManagerController( @RequestParam("code") authCode: String?, @RequestParam state: String?, ): Any { - return if (state == null) { - ResponseEntity.badRequest().body(ResponseConstant.REQUIRED_PARAMETERS_NOT_SET) - } else { - val jwt = googleCalendarAdapter.addAnotherEmailOauth2Callback(authCode, state, false) - return RedirectView("koja-login-callback://callback?token=$jwt") + return when { + authCode.isNullOrEmpty() || state.isNullOrEmpty() -> { + ResponseEntity.badRequest().body(ResponseConstant.REQUIRED_PARAMETERS_NOT_SET) + } + else -> { + try { + val jwt = googleCalendarAdapter.addAnotherEmailOauth2Callback(authCode, state, true) + return RedirectView("koja-login-callback://callback?token=$jwt") + } catch (e: Exception) { + if (e.message.equals(ResponseConstant.USER_ALREADY_EXISTS)) { + return RedirectView("koja-login-callback://callback?error=${ResponseConstant.USER_ALREADY_EXISTS}") + } + ResponseEntity.internalServerError().body(ResponseConstant.GENERIC_INTERNAL_ERROR) + } + } } } @@ -50,10 +71,16 @@ class UserAccountManagerController( @RequestHeader(HeaderConstant.AUTHORISATION) token: String?, @RequestBody email: String?, ): Any { - return if (token == null || email == null) { - ResponseEntity.badRequest().body(ResponseConstant.REQUIRED_PARAMETERS_NOT_SET) - } else { - return userAccountManagerService.deleteGoogleAccount(token, email) + return when { + token.isNullOrEmpty() || email.isNullOrEmpty() -> { + ResponseEntity.badRequest().body(ResponseConstant.REQUIRED_PARAMETERS_NOT_SET) + } + !TokenManagerController.isTokenValid(token) -> { + ResponseEntity.badRequest().body(ResponseConstant.INVALID_TOKEN) + } + else -> { + userAccountManagerService.deleteGoogleAccount(token, email) + } } } } diff --git a/src/main/kotlin/com/teamcaffeine/koja/controller/UserController.kt b/src/main/kotlin/com/teamcaffeine/koja/controller/UserController.kt index 37b586cb..9b71c718 100644 --- a/src/main/kotlin/com/teamcaffeine/koja/controller/UserController.kt +++ b/src/main/kotlin/com/teamcaffeine/koja/controller/UserController.kt @@ -29,39 +29,51 @@ class UserController( private val timeBoundaryRepository: TimeBoundaryRepository, ) { @GetMapping("linked-emails") - fun getUserEmails(@RequestHeader(HeaderConstant.AUTHORISATION) token: String?): ResponseEntity> { - return if (token == null) { - ResponseEntity.badRequest().body(listOf(ResponseConstant.REQUIRED_PARAMETERS_NOT_SET)) - } else { - val jwtTokenData = getUserJWTTokenData(token) - val userAccounts = userAccountRepository.findByUserID(jwtTokenData.userID) + fun getUserEmails(@RequestHeader(HeaderConstant.AUTHORISATION) token: String?): ResponseEntity { + return when { + token.isNullOrEmpty() -> { + ResponseEntity.badRequest().body(listOf(ResponseConstant.REQUIRED_PARAMETERS_NOT_SET)) + } + !TokenManagerController.isTokenValid(token) -> { + ResponseEntity.badRequest().body(ResponseConstant.INVALID_TOKEN) + } + else -> { + val jwtTokenData = getUserJWTTokenData(token) + val userAccounts = userAccountRepository.findByUserID(jwtTokenData.userID) - ResponseEntity.ok(userAccounts.map { it.email }) + ResponseEntity.ok(userAccounts.map { it.email }) + } } } @PostMapping("delete-account") fun deleteUserAccount(@RequestHeader(HeaderConstant.AUTHORISATION) token: String?): ResponseEntity { - return if (token == null) { - ResponseEntity.badRequest().body(ResponseConstant.REQUIRED_PARAMETERS_NOT_SET) - } else { - val jwtTokenData = getUserJWTTokenData(token) - val userAccounts = userAccountRepository.findByUserID(jwtTokenData.userID) - val users = mutableListOf() - val timeBoundaries = mutableListOf() + return when { + token.isNullOrEmpty() -> { + ResponseEntity.badRequest().body(ResponseConstant.REQUIRED_PARAMETERS_NOT_SET) + } + !TokenManagerController.isTokenValid(token) -> { + ResponseEntity.badRequest().body(ResponseConstant.INVALID_TOKEN) + } + else -> { + val jwtTokenData = getUserJWTTokenData(token) + val userAccounts = userAccountRepository.findByUserID(jwtTokenData.userID) + val users = mutableListOf() + val timeBoundaries = mutableListOf() - userAccounts.forEach { - it.user?.let { user -> - users.add(user) - timeBoundaries.addAll(user.getUserTimeBoundaries()) + userAccounts.forEach { + it.user?.let { user -> + users.add(user) + timeBoundaries.addAll(user.getUserTimeBoundaries()) + } + userAccountRepository.delete(it) } - userAccountRepository.delete(it) - } - timeBoundaries.forEach { timeBoundaryRepository.delete(it) } - users.forEach { userRepository.delete(it) } + timeBoundaries.forEach { timeBoundaryRepository.delete(it) } + users.forEach { userRepository.delete(it) } - ResponseEntity.ok(ResponseConstant.ACCOUNT_DELETED) + ResponseEntity.ok(ResponseConstant.ACCOUNT_DELETED) + } } } @@ -73,54 +85,79 @@ class UserController( @RequestParam("endTime")endTime: String?, @RequestParam("type")type: String = "allowed", ): ResponseEntity { - return if (token == null) { - ResponseEntity.badRequest().body(ResponseConstant.REQUIRED_PARAMETERS_NOT_SET) - } else { - val boundary = TimeBoundary(name, startTime, endTime, TimeBoundaryType.ALLOWED) - if (userCalendarService.addTimeBoundary(token, boundary)) { - return ResponseEntity.ok("Time boundary successfully added") - } else { - return ResponseEntity.badRequest().body("Something went wrong") + return when { + token.isNullOrEmpty() -> { + ResponseEntity.badRequest().body(ResponseConstant.REQUIRED_PARAMETERS_NOT_SET) + } + !TokenManagerController.isTokenValid(token) -> { + ResponseEntity.badRequest().body(ResponseConstant.INVALID_TOKEN) + } + else -> { + val boundary = TimeBoundary(name, startTime, endTime, getTimeBoundaryType(type)) + if (userCalendarService.addTimeBoundary(token, boundary)) { + ResponseEntity.ok(ResponseConstant.SUCCESSFULLY_ADDED_TIME_BOUNDARY) + } else { + ResponseEntity.badRequest().body(ResponseConstant.SET_TIME_BOUNDARY_FAILED_INTERNAL_ERROR) + } } } } @PostMapping("/removeTimeBoundary") fun removeTimeBoundary(@RequestHeader(HeaderConstant.AUTHORISATION) token: String?, @RequestParam("name") name: String?): ResponseEntity { - return if (token == null) { - ResponseEntity.badRequest().body(ResponseConstant.REQUIRED_PARAMETERS_NOT_SET) - } else { - if (userCalendarService.removeTimeBoundary(token, name)) { - return ResponseEntity.ok("Time boundary successfully removed") - } else { - return ResponseEntity.badRequest().body("Something went wrong") + return when { + token.isNullOrEmpty() -> { + ResponseEntity.badRequest().body(ResponseConstant.REQUIRED_PARAMETERS_NOT_SET) + } + !TokenManagerController.isTokenValid(token) -> { + ResponseEntity.badRequest().body(ResponseConstant.INVALID_TOKEN) + } + else -> { + if (userCalendarService.removeTimeBoundary(token, name)) { + ResponseEntity.ok(ResponseConstant.SUCCESSFULLY_REMOVED_TIME_BOUNDARY) + } else { + ResponseEntity.badRequest().body(ResponseConstant.SET_TIME_BOUNDARY_FAILED_INTERNAL_ERROR) + } } } } @GetMapping("/getAllTimeBoundary") fun getTimeBoundaries(@RequestHeader(HeaderConstant.AUTHORISATION) token: String?): ResponseEntity { - return if (token == null) { - ResponseEntity.badRequest().body(ResponseConstant.REQUIRED_PARAMETERS_NOT_SET) - } else { - try { - val gson = GsonBuilder().setLenient().excludeFieldsWithoutExposeAnnotation().create() - return ResponseEntity.ok(gson.toJson(userCalendarService.getUserTimeBoundaries(token))) - } catch (e: Exception) { - return ResponseEntity.badRequest().body("Something went wrong.") + return when { + token.isNullOrEmpty() -> { + ResponseEntity.badRequest().body(ResponseConstant.REQUIRED_PARAMETERS_NOT_SET) + } + !TokenManagerController.isTokenValid(token) -> { + ResponseEntity.badRequest().body(ResponseConstant.INVALID_TOKEN) + } + else -> { + try { + val gson = GsonBuilder().setLenient().excludeFieldsWithoutExposeAnnotation().create() + ResponseEntity.ok(gson.toJson(userCalendarService.getUserTimeBoundaries(token))) + } catch (e: Exception) { + ResponseEntity.badRequest().body(ResponseConstant.GENERIC_INTERNAL_ERROR) + } } } } @GetMapping("/getTimeBoundaryAndLocation") fun getTimeBoundaryAndLocation(@RequestHeader(HeaderConstant.AUTHORISATION) token: String?, @RequestParam("location") location: String): ResponseEntity { - return if (token == null) { - ResponseEntity.badRequest().body(ResponseConstant.REQUIRED_PARAMETERS_NOT_SET) - } else { - val gson = Gson() - return ResponseEntity.ok(gson.toJson(userCalendarService.getUserTimeBoundaryAndLocation(token, location))) + return when { + token.isNullOrEmpty() -> { + ResponseEntity.badRequest().body(ResponseConstant.REQUIRED_PARAMETERS_NOT_SET) + } + !TokenManagerController.isTokenValid(token) -> { + ResponseEntity.badRequest().body(ResponseConstant.INVALID_TOKEN) + } + else -> { + val gson = Gson() + ResponseEntity.ok(gson.toJson(userCalendarService.getUserTimeBoundaryAndLocation(token, location))) + } } } + private fun getTimeBoundaryType(timeBoundaryType: String): TimeBoundaryType { return when (timeBoundaryType) { "allowed" -> TimeBoundaryType.ALLOWED diff --git a/src/main/kotlin/com/teamcaffeine/koja/service/GoogleCalendarAdapterService.kt b/src/main/kotlin/com/teamcaffeine/koja/service/GoogleCalendarAdapterService.kt index 91314cfc..954a8d2c 100644 --- a/src/main/kotlin/com/teamcaffeine/koja/service/GoogleCalendarAdapterService.kt +++ b/src/main/kotlin/com/teamcaffeine/koja/service/GoogleCalendarAdapterService.kt @@ -23,6 +23,7 @@ import com.google.maps.GeoApiContext import com.teamcaffeine.koja.constants.EnvironmentVariableConstant import com.teamcaffeine.koja.constants.ExceptionMessageConstant import com.teamcaffeine.koja.constants.Frequency +import com.teamcaffeine.koja.constants.ResponseConstant import com.teamcaffeine.koja.controller.TokenManagerController import com.teamcaffeine.koja.controller.TokenManagerController.Companion.createToken import com.teamcaffeine.koja.controller.TokenRequest @@ -292,7 +293,7 @@ class GoogleCalendarAdapterService( val jwtToken: String if (existingUser != null) { - throw Exception("Email already exits.") + throw Exception(ResponseConstant.USER_ALREADY_EXISTS) } else { if (token == null) { throw Exception("Token is not set.") diff --git a/src/test/kotlin/com/teamcaffiene/koja/controller/CalendarControllerUnitTest.kt b/src/test/kotlin/com/teamcaffiene/koja/controller/CalendarControllerUnitTest.kt index ac3a733f..506aac67 100644 --- a/src/test/kotlin/com/teamcaffiene/koja/controller/CalendarControllerUnitTest.kt +++ b/src/test/kotlin/com/teamcaffiene/koja/controller/CalendarControllerUnitTest.kt @@ -3,10 +3,16 @@ package com.teamcaffiene.koja.controller import com.google.api.client.util.DateTime import com.google.api.services.calendar.model.Event import com.google.api.services.calendar.model.EventDateTime +import com.teamcaffeine.koja.constants.EnvironmentVariableConstant import com.teamcaffeine.koja.constants.ResponseConstant import com.teamcaffeine.koja.controller.CalendarController +import com.teamcaffeine.koja.controller.TokenManagerController +import com.teamcaffeine.koja.controller.TokenRequest +import com.teamcaffeine.koja.dto.JWTGoogleDTO import com.teamcaffeine.koja.dto.UserEventDTO +import com.teamcaffeine.koja.enums.AuthProviderEnum import com.teamcaffeine.koja.service.UserCalendarService +import io.github.cdimascio.dotenv.Dotenv import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test @@ -28,20 +34,57 @@ class CalendarControllerUnitTest { @InjectMocks private lateinit var calendarController: CalendarController + private lateinit var dotenv: Dotenv + @BeforeEach fun setup() { MockitoAnnotations.openMocks(this) + importEnvironmentVariables() + } + + private fun importEnvironmentVariables() { + dotenv = Dotenv.load() + + dotenv[EnvironmentVariableConstant.KOJA_AWS_RDS_DATABASE_URL]?.let { + System.setProperty(EnvironmentVariableConstant.KOJA_AWS_RDS_DATABASE_URL, it) + } + dotenv[EnvironmentVariableConstant.KOJA_AWS_RDS_DATABASE_ADMIN_USERNAME]?.let { + System.setProperty(EnvironmentVariableConstant.KOJA_AWS_RDS_DATABASE_ADMIN_USERNAME, it) + } + dotenv[EnvironmentVariableConstant.KOJA_AWS_RDS_DATABASE_ADMIN_PASSWORD]?.let { + System.setProperty(EnvironmentVariableConstant.KOJA_AWS_RDS_DATABASE_ADMIN_PASSWORD, it) + } + dotenv[EnvironmentVariableConstant.GOOGLE_CLIENT_ID]?.let { + System.setProperty(EnvironmentVariableConstant.GOOGLE_CLIENT_ID, it) + } + dotenv[EnvironmentVariableConstant.GOOGLE_CLIENT_SECRET]?.let { + System.setProperty(EnvironmentVariableConstant.GOOGLE_CLIENT_SECRET, it) + } + dotenv[EnvironmentVariableConstant.GOOGLE_MAPS_API_KEY]?.let { + System.setProperty(EnvironmentVariableConstant.GOOGLE_MAPS_API_KEY, it) + } + dotenv[EnvironmentVariableConstant.KOJA_JWT_SECRET]?.let { + System.setProperty(EnvironmentVariableConstant.KOJA_JWT_SECRET, it) + } } @Test fun testGetAllUserEvents() { // Mock the behavior of the userCalendarService - val token = "testToken" + val mockUserID = Int.MAX_VALUE + val authDetails = JWTGoogleDTO("access", "refresh", 60 * 60) + val mockToken = TokenManagerController.createToken( + TokenRequest( + arrayListOf(authDetails), + AuthProviderEnum.GOOGLE, + mockUserID, + ), + ) val userEvents = arrayListOf() - `when`(userCalendarService.getAllUserEvents(token)).thenReturn(userEvents) + `when`(userCalendarService.getAllUserEvents(mockToken)).thenReturn(userEvents) // Invoke the getAllUserEvents method in the calendarController - val responseEntity: ResponseEntity = calendarController.getAllUserEvents(token) + val responseEntity: ResponseEntity = calendarController.getAllUserEvents(mockToken) // Verify the result assertEquals(HttpStatus.OK, responseEntity.statusCode) @@ -50,12 +93,20 @@ class CalendarControllerUnitTest { @Test fun test_empty_userEvent_list() { - val token = "token" + val mockUserID = Int.MAX_VALUE + val authDetails = JWTGoogleDTO("access", "refresh", 60 * 60) + val mockToken = TokenManagerController.createToken( + TokenRequest( + arrayListOf(authDetails), + AuthProviderEnum.GOOGLE, + mockUserID, + ), + ) val userEvents = emptyList() - `when`(userCalendarService.getAllUserEvents(token)).thenReturn(userEvents) + `when`(userCalendarService.getAllUserEvents(mockToken)).thenReturn(userEvents) - val responseEntity: ResponseEntity = calendarController.getAllUserEvents(token) + val responseEntity: ResponseEntity = calendarController.getAllUserEvents(mockToken) assertEquals(userEvents, responseEntity.body) } @@ -63,15 +114,23 @@ class CalendarControllerUnitTest { @Test fun `test setSuggestedCalendar with valid input`() { // Arrange - val token = "validToken" + val mockUserID = Int.MAX_VALUE + val authDetails = JWTGoogleDTO("access", "refresh", 60 * 60) + val mockToken = TokenManagerController.createToken( + TokenRequest( + arrayListOf(authDetails), + AuthProviderEnum.GOOGLE, + mockUserID, + ), + ) val eventList = arrayListOf() // Act - val response = calendarController.setSuggestedCalendar(token, eventList) + val response = calendarController.setSuggestedCalendar(mockToken, eventList) // Assert assert(response.statusCode == HttpStatus.OK) - assert(response.body == "New calendar successfully created.") + assert(response.body == ResponseConstant.USER_CALENDAR_SET) } @Test @@ -90,7 +149,15 @@ class CalendarControllerUnitTest { @Test fun `rescheduleEvent should update event and return OK`() { - val token = "valid_token" + val mockUserID = Int.MAX_VALUE + val authDetails = JWTGoogleDTO("access", "refresh", 60 * 60) + val mockToken = TokenManagerController.createToken( + TokenRequest( + arrayListOf(authDetails), + AuthProviderEnum.GOOGLE, + mockUserID, + ), + ) val event = UserEventDTO(Event().setId("minimanimo").setStart(EventDateTime().setDate(DateTime("2022-03-15"))).setEnd(EventDateTime().setDate(DateTime("2022-03-16")))) event.setDuration(60) val timeZoneId = ZoneId.of("Africa/Johannesburg") @@ -103,9 +170,9 @@ class CalendarControllerUnitTest { val endTimeUpdated = currentTime.plusMinutes(60) // doNothing().`when`(userCalendarService.deleteEvent(token, "", startTime, endTime)) // doNothing().`when`(userCalendarService.createEvent(token, event)) - `when`(userCalendarService.getAllUserEvents(token)).thenReturn(listOf(event)) + `when`(userCalendarService.getAllUserEvents(mockToken)).thenReturn(listOf(event)) - val response = calendarController.rescheduleEvent(token, event) + val response = calendarController.rescheduleEvent(mockToken, event) assert(response.statusCode == HttpStatus.OK) assert(response.body == ResponseConstant.EVENT_UPDATED) diff --git a/src/test/kotlin/com/teamcaffiene/koja/controller/UserControllerUnitTest.kt b/src/test/kotlin/com/teamcaffiene/koja/controller/UserControllerUnitTest.kt index 19cb352b..4de55ac4 100644 --- a/src/test/kotlin/com/teamcaffiene/koja/controller/UserControllerUnitTest.kt +++ b/src/test/kotlin/com/teamcaffiene/koja/controller/UserControllerUnitTest.kt @@ -4,6 +4,7 @@ import com.google.gson.Gson import com.google.gson.GsonBuilder import com.teamcaffeine.koja.constants.EnvironmentVariableConstant import com.teamcaffeine.koja.constants.ResponseConstant +import com.teamcaffeine.koja.controller.TokenManagerController import com.teamcaffeine.koja.controller.TokenManagerController.Companion.createToken import com.teamcaffeine.koja.controller.TokenRequest import com.teamcaffeine.koja.controller.UserController @@ -161,21 +162,28 @@ class UserControllerUnitTest { @Test fun `test addTimeBoundary with valid parameters`() { // Mock request parameters - val token = "your_token_here" + val mockUserID = Int.MAX_VALUE + val authDetails = JWTGoogleDTO("access", "refresh", 60 * 60) + val mockToken = TokenManagerController.createToken( + TokenRequest( + arrayListOf(authDetails), + AuthProviderEnum.GOOGLE, + mockUserID, + ), + ) val name = "Play" val startTime = "2023-07-30T12:00:00Z" val endTime = "2023-07-30T14:00:00Z" val timeBoundary = TimeBoundary(name, startTime, endTime) // Mock the userCalendarService.addTimeBoundary method - val expectedResult = "Time boundary successfully added" - whenever(userCalendarService.addTimeBoundary(token, timeBoundary)).thenReturn(true) + whenever(userCalendarService.addTimeBoundary(mockToken, timeBoundary)).thenReturn(true) // Call the function and capture the response - val response = userController.addTimeBoundary(token, name, startTime, endTime) + val response = userController.addTimeBoundary(mockToken, name, startTime, endTime) // Check the response status and body assertEquals(HttpStatus.OK, response.statusCode) - assertEquals(expectedResult, response.body) + assertEquals(ResponseConstant.SUCCESSFULLY_ADDED_TIME_BOUNDARY, response.body) } @Test @@ -198,41 +206,57 @@ class UserControllerUnitTest { @Test fun `test addTimeBoundary with userCalendarService returning false`() { // Mock request parameters - val token = "your_token_here" + val mockUserID = Int.MAX_VALUE + val authDetails = JWTGoogleDTO("access", "refresh", 60 * 60) + val mockToken = TokenManagerController.createToken( + TokenRequest( + arrayListOf(authDetails), + AuthProviderEnum.GOOGLE, + mockUserID, + ), + ) val name = "Play" val startTime = "2023-07-30T12:00:00Z" val endTime = "2023-07-30T14:00:00Z" val timeBoundary = TimeBoundary(name, startTime, endTime) // Mock the userCalendarService.removeTimeBoundary method to return false - whenever(userCalendarService.addTimeBoundary(token, timeBoundary)).thenReturn(false) + whenever(userCalendarService.addTimeBoundary(mockToken, timeBoundary)).thenReturn(false) // Call the function and capture the response - val response = userController.addTimeBoundary(token, name, startTime, endTime) + val response = userController.addTimeBoundary(mockToken, name, startTime, endTime) // Check the response status and body assertEquals(HttpStatus.BAD_REQUEST, response.statusCode) - assertEquals("Something went wrong", response.body) + assertEquals(ResponseConstant.SET_TIME_BOUNDARY_FAILED_INTERNAL_ERROR, response.body) } @Test fun `test removeTimeBoundary with valid parameters`() { // Mock request parameters - val token = "your_token_here" + val mockUserID = Int.MAX_VALUE + val authDetails = JWTGoogleDTO("access", "refresh", 60 * 60) + val mockToken = TokenManagerController.createToken( + TokenRequest( + arrayListOf(authDetails), + AuthProviderEnum.GOOGLE, + mockUserID, + ), + ) val name = "Boundary Name" // Mock the userCalendarService.removeTimeBoundary method - whenever(userCalendarService.removeTimeBoundary(eq(token), eq(name))).thenReturn(true) + whenever(userCalendarService.removeTimeBoundary(eq(mockToken), eq(name))).thenReturn(true) // Call the function and capture the response - val response = userController.removeTimeBoundary(token, name) + val response = userController.removeTimeBoundary(mockToken, name) // Verify the userCalendarService.removeTimeBoundary method was called with the correct parameters - verify(userCalendarService).removeTimeBoundary(eq(token), eq(name)) + verify(userCalendarService).removeTimeBoundary(eq(mockToken), eq(name)) // Check the response status and body assertEquals(HttpStatus.OK, response.statusCode) - assertEquals("Time boundary successfully removed", response.body) + assertEquals(ResponseConstant.SUCCESSFULLY_REMOVED_TIME_BOUNDARY, response.body) } @Test @@ -251,39 +275,55 @@ class UserControllerUnitTest { @Test fun `test removeTimeBoundary with userCalendarService returning false`() { // Mock request parameters - val token = "your_token_here" + val mockUserID = Int.MAX_VALUE + val authDetails = JWTGoogleDTO("access", "refresh", 60 * 60) + val mockToken = TokenManagerController.createToken( + TokenRequest( + arrayListOf(authDetails), + AuthProviderEnum.GOOGLE, + mockUserID, + ), + ) val name = "Boundary Name" // Mock the userCalendarService.removeTimeBoundary method to return false - whenever(userCalendarService.removeTimeBoundary(eq(token), eq(name))).thenReturn(false) + whenever(userCalendarService.removeTimeBoundary(eq(mockToken), eq(name))).thenReturn(false) // Call the function and capture the response - val response = userController.removeTimeBoundary(token, name) + val response = userController.removeTimeBoundary(mockToken, name) // Verify the userCalendarService.removeTimeBoundary method was called with the correct parameters - verify(userCalendarService).removeTimeBoundary(eq(token), eq(name)) + verify(userCalendarService).removeTimeBoundary(eq(mockToken), eq(name)) // Check the response status and body assertEquals(HttpStatus.BAD_REQUEST, response.statusCode) - assertEquals("Something went wrong", response.body) + assertEquals(ResponseConstant.SET_TIME_BOUNDARY_FAILED_INTERNAL_ERROR, response.body) } @Test fun `test getTimeBoundaries with valid token`() { // Mock request parameters - val token = "your_token_here" + val mockUserID = Int.MAX_VALUE + val authDetails = JWTGoogleDTO("access", "refresh", 60 * 60) + val mockToken = TokenManagerController.createToken( + TokenRequest( + arrayListOf(authDetails), + AuthProviderEnum.GOOGLE, + mockUserID, + ), + ) // Mock the userCalendarService.getUserTimeBoundaries method val boundaries = mutableListOf() val timeBoundary = TimeBoundary("Boundary1", "2023-07-30T12:00:00Z", "2023-07-30T14:00:00Z") boundaries.add(0, timeBoundary) - whenever(userCalendarService.getUserTimeBoundaries(eq(token))).thenReturn(boundaries) + whenever(userCalendarService.getUserTimeBoundaries(eq(mockToken))).thenReturn(boundaries) // Call the function and capture the response - val response = userController.getTimeBoundaries(token) + val response = userController.getTimeBoundaries(mockToken) // Verify the userCalendarService.getUserTimeBoundaries method was called with the correct token - verify(userCalendarService).getUserTimeBoundaries(eq(token)) + verify(userCalendarService).getUserTimeBoundaries(eq(mockToken)) // Check the response status and body assertEquals(HttpStatus.OK, response.statusCode) @@ -306,32 +346,48 @@ class UserControllerUnitTest { @Test fun `test getTimeBoundaries with userCalendarService throwing an exception`() { // Mock request parameters - val token = "your_token_here" + val mockUserID = Int.MAX_VALUE + val authDetails = JWTGoogleDTO("access", "refresh", 60 * 60) + val mockToken = TokenManagerController.createToken( + TokenRequest( + arrayListOf(authDetails), + AuthProviderEnum.GOOGLE, + mockUserID, + ), + ) // Mock the userCalendarService.getUserTimeBoundaries method to throw an exception - whenever(userCalendarService.getUserTimeBoundaries(eq(token))).thenThrow(RuntimeException("Error fetching boundaries.")) + whenever(userCalendarService.getUserTimeBoundaries(eq(mockToken))).thenThrow(RuntimeException("Error fetching boundaries.")) // Call the function and capture the response - val response = userController.getTimeBoundaries(token) + val response = userController.getTimeBoundaries(mockToken) // Verify the userCalendarService.getUserTimeBoundaries method was called with the correct token - verify(userCalendarService).getUserTimeBoundaries(eq(token)) + verify(userCalendarService).getUserTimeBoundaries(eq(mockToken)) // Check the response status and body assertEquals(HttpStatus.BAD_REQUEST, response.statusCode) - assertEquals("Something went wrong.", response.body) + assertEquals(ResponseConstant.GENERIC_INTERNAL_ERROR, response.body) } @Test fun `test getTimeBoundaryAndLocation with valid token and location`() { // Mock request parameters - val token = "your_token_here" + val mockUserID = Int.MAX_VALUE + val authDetails = JWTGoogleDTO("access", "refresh", 60 * 60) + val mockToken = TokenManagerController.createToken( + TokenRequest( + arrayListOf(authDetails), + AuthProviderEnum.GOOGLE, + mockUserID, + ), + ) val location = "London" // Mock the userCalendarService.getUserTimeBoundaryAndLocation method val boundary = TimeBoundary("Boundary1", "2023-07-30T12:00:00Z", "2023-07-30T14:00:00Z") - whenever(userCalendarService.getUserTimeBoundaryAndLocation(eq(token), eq(location))).thenReturn(Pair(boundary, location)) + whenever(userCalendarService.getUserTimeBoundaryAndLocation(eq(mockToken), eq(location))).thenReturn(Pair(boundary, location)) // Call the function and capture the response - val response = userController.getTimeBoundaryAndLocation(token, location) + val response = userController.getTimeBoundaryAndLocation(mockToken, location) // Verify the userCalendarService.getUserTimeBoundaryAndLocation method was called with the correct parameters - verify(userCalendarService).getUserTimeBoundaryAndLocation(eq(token), eq(location)) + verify(userCalendarService).getUserTimeBoundaryAndLocation(eq(mockToken), eq(location)) // Check the response status and body assertEquals(HttpStatus.OK, response.statusCode) // Convert the boundaryAndLocation to JSON using Gson and check the response body diff --git a/src/test/kotlin/com/teamcaffiene/koja/service/UserCalendarServiceTest.kt b/src/test/kotlin/com/teamcaffiene/koja/service/UserCalendarServiceTest.kt index 877ef612..1fa45ad4 100644 --- a/src/test/kotlin/com/teamcaffiene/koja/service/UserCalendarServiceTest.kt +++ b/src/test/kotlin/com/teamcaffiene/koja/service/UserCalendarServiceTest.kt @@ -14,17 +14,22 @@ import com.teamcaffeine.koja.repository.UserAccountRepository import com.teamcaffeine.koja.repository.UserRepository import com.teamcaffeine.koja.service.UserCalendarService import io.github.cdimascio.dotenv.Dotenv +import io.mockk.every +import io.mockk.mockk +import io.mockk.unmockkAll +import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.BeforeAll import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows +import org.mockito.ArgumentMatchers.anyString import org.mockito.Mock import org.mockito.Mockito import org.mockito.MockitoAnnotations import org.mockito.invocation.InvocationOnMock import org.mockito.kotlin.any import org.mockito.kotlin.check -import org.mockito.kotlin.eq import org.mockito.kotlin.spy import org.mockito.kotlin.whenever import java.util.Optional @@ -43,84 +48,89 @@ class UserCalendarServiceTest { private lateinit var userCalendarService: UserCalendarService private lateinit var dotenv: Dotenv - init { - importEnvironmentVariables() + companion object { + @JvmStatic + @BeforeAll + fun setupOnce() { + val dotenv: Dotenv = Dotenv.load() + System.setProperty( + EnvironmentVariableConstant.KOJA_AWS_RDS_DATABASE_URL, + dotenv[EnvironmentVariableConstant.KOJA_AWS_RDS_DATABASE_URL]!!, + ) + System.setProperty( + EnvironmentVariableConstant.KOJA_AWS_RDS_DATABASE_ADMIN_USERNAME, + dotenv[EnvironmentVariableConstant.KOJA_AWS_RDS_DATABASE_ADMIN_USERNAME]!!, + ) + System.setProperty( + EnvironmentVariableConstant.KOJA_AWS_RDS_DATABASE_ADMIN_PASSWORD, + dotenv[EnvironmentVariableConstant.KOJA_AWS_RDS_DATABASE_ADMIN_PASSWORD]!!, + ) + System.setProperty( + EnvironmentVariableConstant.KOJA_AWS_DYNAMODB_ACCESS_KEY_ID, + dotenv[EnvironmentVariableConstant.KOJA_AWS_DYNAMODB_ACCESS_KEY_ID]!!, + ) + System.setProperty( + EnvironmentVariableConstant.KOJA_AWS_DYNAMODB_ACCESS_KEY_SECRET, + dotenv[EnvironmentVariableConstant.KOJA_AWS_DYNAMODB_ACCESS_KEY_SECRET]!!, + ) + System.setProperty( + EnvironmentVariableConstant.OPENAI_API_KEY, + dotenv[EnvironmentVariableConstant.OPENAI_API_KEY]!!, + ) + System.setProperty( + EnvironmentVariableConstant.SERVER_ADDRESS, + dotenv[EnvironmentVariableConstant.SERVER_ADDRESS]!!, + ) + System.setProperty( + EnvironmentVariableConstant.SERVER_PORT, + dotenv[EnvironmentVariableConstant.SERVER_PORT] ?: "", + ) + // Set Google Sign In client ID and client secret properties + System.setProperty( + EnvironmentVariableConstant.GOOGLE_CLIENT_ID, + dotenv[EnvironmentVariableConstant.GOOGLE_CLIENT_ID]!!, + ) + System.setProperty( + EnvironmentVariableConstant.GOOGLE_CLIENT_SECRET, + dotenv[EnvironmentVariableConstant.GOOGLE_CLIENT_SECRET]!!, + ) + System.setProperty( + EnvironmentVariableConstant.GOOGLE_MAPS_API_KEY, + dotenv[EnvironmentVariableConstant.GOOGLE_MAPS_API_KEY]!!, + ) + // Set JWT secret key and other related properties + System.setProperty( + EnvironmentVariableConstant.KOJA_JWT_SECRET, + dotenv[EnvironmentVariableConstant.KOJA_JWT_SECRET]!!, + ) + System.setProperty( + EnvironmentVariableConstant.KOJA_ID_SECRET, + dotenv[EnvironmentVariableConstant.KOJA_ID_SECRET]!!, + ) + System.setProperty( + EnvironmentVariableConstant.KOJA_PRIVATE_KEY_PASS, + dotenv[EnvironmentVariableConstant.KOJA_PRIVATE_KEY_PASS]!!, + ) + System.setProperty( + EnvironmentVariableConstant.KOJA_PRIVATE_KEY_SALT, + dotenv[EnvironmentVariableConstant.KOJA_PRIVATE_KEY_SALT]!!, + ) + } } @BeforeEach fun setup() { MockitoAnnotations.openMocks(this) - importEnvironmentVariables() - userCalendarService = spy(UserCalendarService(userRepository, jwtFunctionality)) - } - private fun importEnvironmentVariables() { - val dotenv: Dotenv = Dotenv.load() + val tokenManagerController = mockk() + every { tokenManagerController.isTokenValid(anyString()) } returns true + } - System.setProperty( - EnvironmentVariableConstant.KOJA_AWS_RDS_DATABASE_URL, - dotenv[EnvironmentVariableConstant.KOJA_AWS_RDS_DATABASE_URL]!!, - ) - System.setProperty( - EnvironmentVariableConstant.KOJA_AWS_RDS_DATABASE_ADMIN_USERNAME, - dotenv[EnvironmentVariableConstant.KOJA_AWS_RDS_DATABASE_ADMIN_USERNAME]!!, - ) - System.setProperty( - EnvironmentVariableConstant.KOJA_AWS_RDS_DATABASE_ADMIN_PASSWORD, - dotenv[EnvironmentVariableConstant.KOJA_AWS_RDS_DATABASE_ADMIN_PASSWORD]!!, - ) - System.setProperty( - EnvironmentVariableConstant.KOJA_AWS_DYNAMODB_ACCESS_KEY_ID, - dotenv[EnvironmentVariableConstant.KOJA_AWS_DYNAMODB_ACCESS_KEY_ID]!!, - ) - System.setProperty( - EnvironmentVariableConstant.KOJA_AWS_DYNAMODB_ACCESS_KEY_SECRET, - dotenv[EnvironmentVariableConstant.KOJA_AWS_DYNAMODB_ACCESS_KEY_SECRET]!!, - ) - System.setProperty( - EnvironmentVariableConstant.OPENAI_API_KEY, - dotenv[EnvironmentVariableConstant.OPENAI_API_KEY]!!, - ) - System.setProperty( - EnvironmentVariableConstant.SERVER_ADDRESS, - dotenv[EnvironmentVariableConstant.SERVER_ADDRESS]!!, - ) - System.setProperty( - EnvironmentVariableConstant.SERVER_PORT, - dotenv[EnvironmentVariableConstant.SERVER_PORT] ?: "", - ) - // Set Google Sign In client ID and client secret properties - System.setProperty( - EnvironmentVariableConstant.GOOGLE_CLIENT_ID, - dotenv[EnvironmentVariableConstant.GOOGLE_CLIENT_ID]!!, - ) - System.setProperty( - EnvironmentVariableConstant.GOOGLE_CLIENT_SECRET, - dotenv[EnvironmentVariableConstant.GOOGLE_CLIENT_SECRET]!!, - ) - System.setProperty( - EnvironmentVariableConstant.GOOGLE_MAPS_API_KEY, - dotenv[EnvironmentVariableConstant.GOOGLE_MAPS_API_KEY]!!, - ) - // Set JWT secret key and other related properties - System.setProperty( - EnvironmentVariableConstant.KOJA_JWT_SECRET, - dotenv[EnvironmentVariableConstant.KOJA_JWT_SECRET]!!, - ) - System.setProperty( - EnvironmentVariableConstant.KOJA_ID_SECRET, - dotenv[EnvironmentVariableConstant.KOJA_ID_SECRET]!!, - ) - System.setProperty( - EnvironmentVariableConstant.KOJA_PRIVATE_KEY_PASS, - dotenv[EnvironmentVariableConstant.KOJA_PRIVATE_KEY_PASS]!!, - ) - System.setProperty( - EnvironmentVariableConstant.KOJA_PRIVATE_KEY_SALT, - dotenv[EnvironmentVariableConstant.KOJA_PRIVATE_KEY_SALT]!!, - ) + @AfterEach + fun cleanup() { + unmockkAll() } // @BeforeEach @@ -138,7 +148,6 @@ class UserCalendarServiceTest { @Test fun getAllUserEvents_with_null_token_throws_exception() { - // Arrange val mockUserID = 1 val userAccounts = mutableListOf() val mockUserJWTData = UserJWTTokenDataDTO(userAccounts, mockUserID) @@ -151,12 +160,11 @@ class UserCalendarServiceTest { ), ) val tokenT = "mockToken" - MockitoAnnotations.openMocks(this) - whenever(jwtFunctionality.getUserJWTTokenData(eq(jwtToken))).thenReturn(mockUserJWTData) + whenever(jwtFunctionality.getUserJWTTokenData(jwtToken)).thenReturn(mockUserJWTData) whenever(userAccountRepository.findByUserID(mockUserID)).thenReturn(emptyList()) // Assert - assertThrows { userCalendarService.getAllUserEvents(eq(jwtToken)) } + assertThrows { userCalendarService.getAllUserEvents(jwtToken) } } @Test @@ -338,6 +346,7 @@ class UserCalendarServiceTest { @Test fun `removeTimeBoundary should return false when the user does not exist`() { // Arrange + val mockUserID = Int.MAX_VALUE val userAccounts = mutableListOf() val mockUserJWTData = UserJWTTokenDataDTO(userAccounts, mockUserID)