From 639e1e42dda08fa17612fec76e85ed058e88dd20 Mon Sep 17 00:00:00 2001 From: sardq Date: Fri, 7 Jun 2024 21:11:19 +0400 Subject: [PATCH] hello world --- 1 lab/.gitignore | 37 +++ 1 lab/build.gradle | 27 ++ 1 lab/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 43462 bytes .../gradle/wrapper/gradle-wrapper.properties | 7 + 1 lab/gradlew | 249 ++++++++++++++ 1 lab/gradlew.bat | 92 ++++++ 1 lab/index.html | 64 ++++ 1 lab/settings.gradle | 1 + .../java/com/example/demo/ApiController.java | 54 ++++ .../com/example/demo/DemoApplication.java | 12 + .../main/java/com/example/demo/UserDto.java | 43 +++ .../main/java/com/example/demo/WebConfig.java | 15 + .../src/main/resources/application.properties | 1 + .../example/demo/DemoApplicationTests.java | 13 + 2 lab/.gitignore | 37 +++ ...c3f4083e-43c8-4e91-bcd1-849b3c786de3.vsidx | Bin 0 -> 96427 bytes 2 lab/.vs/IP/FileContentIndex/read.lock | 0 2 lab/.vs/IP/v17/.wsuo | Bin 0 -> 14336 bytes 2 lab/.vs/ProjectSettings.json | 3 + 2 lab/.vs/VSWorkspaceState.json | 7 + 2 lab/.vs/slnx.sqlite | Bin 0 -> 90112 bytes 2 lab/build.gradle | 28 ++ 2 lab/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 43462 bytes .../gradle/wrapper/gradle-wrapper.properties | 7 + 2 lab/gradlew | 249 ++++++++++++++ 2 lab/gradlew.bat | 92 ++++++ 2 lab/settings.gradle | 1 + .../com/example/demo/DemoApplication.java | 85 +++++ .../demo/comments/api/CommentController.java | 78 +++++ .../example/demo/comments/api/CommentDto.java | 62 ++++ .../demo/comments/model/CommentEntity.java | 78 +++++ .../repository/CommentRepository.java | 10 + .../demo/comments/service/CommentService.java | 43 +++ .../demo/core/configuration/Constants.java | 8 + .../configuration/MapperConfiguration.java | 13 + .../core/configuration/WebConfiguration.java | 15 + .../java/com/example/demo/core/convert.java | 17 + .../demo/core/error/NotFoundException.java | 7 + .../example/demo/core/model/BaseEntity.java | 20 ++ .../core/repository/CommonRepository.java | 17 + .../demo/core/repository/MapRepository.java | 57 ++++ .../demo/messages/api/MessageController.java | 75 +++++ .../example/demo/messages/api/MessageDto.java | 62 ++++ .../demo/messages/model/MessageEntity.java | 78 +++++ .../repository/MessageRepository.java | 10 + .../demo/messages/service/MessageService.java | 43 +++ .../demo/posts/api/PostController.java | 89 +++++ .../com/example/demo/posts/api/PostDto.java | 73 +++++ .../example/demo/posts/model/PostEntity.java | 89 +++++ .../demo/posts/repository/PostRepository.java | 10 + .../demo/posts/service/PostService.java | 61 ++++ .../demo/users/api/UserController.java | 82 +++++ .../com/example/demo/users/api/UserDto.java | 65 ++++ .../example/demo/users/model/UserEntity.java | 76 +++++ .../demo/users/repository/UserRepository.java | 10 + .../demo/users/service/UserService.java | 57 ++++ .../src/main/resources/application.properties | 1 + .../com/example/demo/CommentServiceTests.java | 92 ++++++ .../com/example/demo/MessageServiceTests.java | 77 +++++ .../com/example/demo/PostServiceTests.java | 81 +++++ .../com/example/demo/UserServiceTests.java | 68 ++++ 3 lab/.gitignore | 36 +++ 3 lab/.vscode/extensions.json | 12 + 3 lab/.vscode/launch.json | 14 + 3 lab/.vscode/settings.json | 21 ++ 3 lab/build.gradle | 43 +++ 3 lab/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 43462 bytes .../gradle/wrapper/gradle-wrapper.properties | 7 + 3 lab/gradlew | 249 ++++++++++++++ 3 lab/gradlew.bat | 92 ++++++ 3 lab/readme.md | 18 ++ 3 lab/settings.gradle | 1 + .../com/example/demo/DemoApplication.java | 87 +++++ .../demo/comments/api/CommentController.java | 81 +++++ .../example/demo/comments/api/CommentDto.java | 62 ++++ .../demo/comments/model/CommentEntity.java | 90 ++++++ .../repository/CommentRepository.java | 14 + .../demo/comments/service/CommentService.java | 63 ++++ .../com/example/demo/core/api/PageDto.java | 97 ++++++ .../example/demo/core/api/PageDtoMapper.java | 25 ++ .../demo/core/configuration/Constants.java | 10 + .../configuration/MapperConfiguration.java | 13 + .../core/configuration/WebConfiguration.java | 15 + .../java/com/example/demo/core/convert.java | 17 + .../demo/core/error/NotFoundException.java | 7 + .../example/demo/core/model/BaseEntity.java | 32 ++ .../demo/messages/api/MessageController.java | 79 +++++ .../example/demo/messages/api/MessageDto.java | 62 ++++ .../demo/messages/model/MessageEntity.java | 91 ++++++ .../repository/MessageRepository.java | 21 ++ .../demo/messages/service/MessageService.java | 72 +++++ .../demo/posts/api/PostController.java | 92 ++++++ .../com/example/demo/posts/api/PostDto.java | 76 +++++ .../example/demo/posts/model/PostEntity.java | 102 ++++++ .../demo/posts/repository/PostRepository.java | 14 + .../demo/posts/service/PostService.java | 86 +++++ .../demo/users/api/UserController.java | 94 ++++++ .../com/example/demo/users/api/UserDto.java | 68 ++++ .../example/demo/users/api/UserTopDto.java | 26 ++ .../example/demo/users/model/UserEntity.java | 92 ++++++ .../com/example/demo/users/model/UserTop.java | 7 + .../demo/users/repository/UserRepository.java | 26 ++ .../demo/users/service/UserService.java | 95 ++++++ .../src/main/resources/application.properties | 21 ++ .../com/example/demo/CommentServiceTests.java | 109 +++++++ .../com/example/demo/MessageServiceTests.java | 97 ++++++ .../com/example/demo/PostServiceTests.java | 115 +++++++ .../com/example/demo/UserServiceTests.java | 89 +++++ .../src/test/resources/application.properties | 14 + 5 lab/.gitignore | 36 +++ 5 lab/.vscode/extensions.json | 12 + 5 lab/.vscode/launch.json | 14 + 5 lab/.vscode/settings.json | 24 ++ 5 lab/build.gradle | 51 +++ 5 lab/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 43462 bytes .../gradle/wrapper/gradle-wrapper.properties | 7 + 5 lab/gradlew | 249 ++++++++++++++ 5 lab/gradlew.bat | 92 ++++++ 5 lab/readme.md | 15 + 5 lab/settings.gradle | 1 + .../com/example/demo/DemoApplication.java | 101 ++++++ .../demo/comments/api/CommentController.java | 138 ++++++++ .../example/demo/comments/api/CommentDto.java | 71 ++++ .../demo/comments/api/PostCommentDto.java | 64 ++++ .../demo/comments/model/CommentEntity.java | 95 ++++++ .../repository/CommentRepository.java | 25 ++ .../demo/comments/service/CommentService.java | 90 ++++++ .../demo/core/api/GlobalController.java | 19 ++ .../demo/core/api/PageAttributesMapper.java | 18 ++ .../demo/core/configuration/Constants.java | 19 ++ .../configuration/MapperConfiguration.java | 23 ++ .../core/configuration/WebConfiguration.java | 13 + .../java/com/example/demo/core/convert.java | 22 ++ .../demo/core/error/AdviceController.java | 53 +++ .../demo/core/error/NotFoundException.java | 7 + .../example/demo/core/model/BaseEntity.java | 28 ++ .../core/security/SecurityConfiguration.java | 63 ++++ .../demo/core/security/UserPrincipal.java | 64 ++++ .../demo/messages/api/DialogController.java | 153 +++++++++ .../example/demo/messages/api/DialogDto.java | 70 ++++ .../demo/messages/api/MessageController.java | 77 +++++ .../example/demo/messages/api/MessageDto.java | 80 +++++ .../demo/messages/model/MessageEntity.java | 96 ++++++ .../repository/MessageRepository.java | 29 ++ .../demo/messages/service/MessageService.java | 65 ++++ .../demo/posts/api/HomeController.java | 305 ++++++++++++++++++ .../example/demo/posts/api/HomePostDto.java | 82 +++++ .../demo/posts/api/PostController.java | 154 +++++++++ .../com/example/demo/posts/api/PostDto.java | 85 +++++ .../example/demo/posts/model/PostEntity.java | 106 ++++++ .../demo/posts/repository/PostRepository.java | 21 ++ .../demo/posts/service/PostService.java | 88 +++++ .../com/example/demo/users/api/FriendDto.java | 27 ++ .../demo/users/api/UserController.java | 144 +++++++++ .../com/example/demo/users/api/UserDto.java | 76 +++++ .../demo/users/api/UserProfileController.java | 146 +++++++++ .../demo/users/api/UserProfileDto.java | 67 ++++ .../demo/users/api/UserSignupController.java | 71 ++++ .../example/demo/users/api/UserSignupDto.java | 51 +++ .../example/demo/users/api/UserTopDto.java | 26 ++ .../demo/users/model/FriendsEntity.java | 7 + .../example/demo/users/model/UserEntity.java | 114 +++++++ .../example/demo/users/model/UserRole.java | 15 + .../com/example/demo/users/model/UserTop.java | 7 + .../demo/users/repository/UserRepository.java | 38 +++ .../example/demo/users/service/Pageable.java | 5 + .../demo/users/service/UserService.java | 156 +++++++++ .../src/main/resources/application.properties | 20 ++ 5 lab/src/main/resources/public/css/style.css | 256 +++++++++++++++ 5 lab/src/main/resources/public/eye.png | Bin 0 -> 6739 bytes 5 lab/src/main/resources/public/favicon.svg | 59 ++++ 5 lab/src/main/resources/public/like.png | Bin 0 -> 9327 bytes 5 lab/src/main/resources/templates.zip | Bin 0 -> 16517 bytes .../resources/templates/comment-edit.html | 28 ++ .../src/main/resources/templates/comment.html | 45 +++ .../main/resources/templates/comments.html | 58 ++++ .../src/main/resources/templates/default.html | 78 +++++ .../src/main/resources/templates/dialog.html | 66 ++++ .../src/main/resources/templates/dialogs.html | 38 +++ 5 lab/src/main/resources/templates/error.html | 37 +++ .../src/main/resources/templates/friends.html | 54 ++++ 5 lab/src/main/resources/templates/home.html | 72 +++++ 5 lab/src/main/resources/templates/login.html | 44 +++ .../src/main/resources/templates/message.html | 47 +++ .../main/resources/templates/pagination.html | 51 +++ .../main/resources/templates/post-edit.html | 26 ++ 5 lab/src/main/resources/templates/post.html | 53 +++ .../src/main/resources/templates/profile.html | 59 ++++ .../src/main/resources/templates/signup.html | 46 +++ .../main/resources/templates/user-edit.html | 36 +++ 5 lab/src/main/resources/templates/user.html | 59 ++++ .../src/test/resources/application.properties | 14 + 192 files changed, 10423 insertions(+) create mode 100644 1 lab/.gitignore create mode 100644 1 lab/build.gradle create mode 100644 1 lab/gradle/wrapper/gradle-wrapper.jar create mode 100644 1 lab/gradle/wrapper/gradle-wrapper.properties create mode 100644 1 lab/gradlew create mode 100644 1 lab/gradlew.bat create mode 100644 1 lab/index.html create mode 100644 1 lab/settings.gradle create mode 100644 1 lab/src/main/java/com/example/demo/ApiController.java create mode 100644 1 lab/src/main/java/com/example/demo/DemoApplication.java create mode 100644 1 lab/src/main/java/com/example/demo/UserDto.java create mode 100644 1 lab/src/main/java/com/example/demo/WebConfig.java create mode 100644 1 lab/src/main/resources/application.properties create mode 100644 1 lab/src/test/java/com/example/demo/DemoApplicationTests.java create mode 100644 2 lab/.gitignore create mode 100644 2 lab/.vs/IP/FileContentIndex/c3f4083e-43c8-4e91-bcd1-849b3c786de3.vsidx create mode 100644 2 lab/.vs/IP/FileContentIndex/read.lock create mode 100644 2 lab/.vs/IP/v17/.wsuo create mode 100644 2 lab/.vs/ProjectSettings.json create mode 100644 2 lab/.vs/VSWorkspaceState.json create mode 100644 2 lab/.vs/slnx.sqlite create mode 100644 2 lab/build.gradle create mode 100644 2 lab/gradle/wrapper/gradle-wrapper.jar create mode 100644 2 lab/gradle/wrapper/gradle-wrapper.properties create mode 100644 2 lab/gradlew create mode 100644 2 lab/gradlew.bat create mode 100644 2 lab/settings.gradle create mode 100644 2 lab/src/main/java/com/example/demo/DemoApplication.java create mode 100644 2 lab/src/main/java/com/example/demo/comments/api/CommentController.java create mode 100644 2 lab/src/main/java/com/example/demo/comments/api/CommentDto.java create mode 100644 2 lab/src/main/java/com/example/demo/comments/model/CommentEntity.java create mode 100644 2 lab/src/main/java/com/example/demo/comments/repository/CommentRepository.java create mode 100644 2 lab/src/main/java/com/example/demo/comments/service/CommentService.java create mode 100644 2 lab/src/main/java/com/example/demo/core/configuration/Constants.java create mode 100644 2 lab/src/main/java/com/example/demo/core/configuration/MapperConfiguration.java create mode 100644 2 lab/src/main/java/com/example/demo/core/configuration/WebConfiguration.java create mode 100644 2 lab/src/main/java/com/example/demo/core/convert.java create mode 100644 2 lab/src/main/java/com/example/demo/core/error/NotFoundException.java create mode 100644 2 lab/src/main/java/com/example/demo/core/model/BaseEntity.java create mode 100644 2 lab/src/main/java/com/example/demo/core/repository/CommonRepository.java create mode 100644 2 lab/src/main/java/com/example/demo/core/repository/MapRepository.java create mode 100644 2 lab/src/main/java/com/example/demo/messages/api/MessageController.java create mode 100644 2 lab/src/main/java/com/example/demo/messages/api/MessageDto.java create mode 100644 2 lab/src/main/java/com/example/demo/messages/model/MessageEntity.java create mode 100644 2 lab/src/main/java/com/example/demo/messages/repository/MessageRepository.java create mode 100644 2 lab/src/main/java/com/example/demo/messages/service/MessageService.java create mode 100644 2 lab/src/main/java/com/example/demo/posts/api/PostController.java create mode 100644 2 lab/src/main/java/com/example/demo/posts/api/PostDto.java create mode 100644 2 lab/src/main/java/com/example/demo/posts/model/PostEntity.java create mode 100644 2 lab/src/main/java/com/example/demo/posts/repository/PostRepository.java create mode 100644 2 lab/src/main/java/com/example/demo/posts/service/PostService.java create mode 100644 2 lab/src/main/java/com/example/demo/users/api/UserController.java create mode 100644 2 lab/src/main/java/com/example/demo/users/api/UserDto.java create mode 100644 2 lab/src/main/java/com/example/demo/users/model/UserEntity.java create mode 100644 2 lab/src/main/java/com/example/demo/users/repository/UserRepository.java create mode 100644 2 lab/src/main/java/com/example/demo/users/service/UserService.java create mode 100644 2 lab/src/main/resources/application.properties create mode 100644 2 lab/src/test/java/com/example/demo/CommentServiceTests.java create mode 100644 2 lab/src/test/java/com/example/demo/MessageServiceTests.java create mode 100644 2 lab/src/test/java/com/example/demo/PostServiceTests.java create mode 100644 2 lab/src/test/java/com/example/demo/UserServiceTests.java create mode 100644 3 lab/.gitignore create mode 100644 3 lab/.vscode/extensions.json create mode 100644 3 lab/.vscode/launch.json create mode 100644 3 lab/.vscode/settings.json create mode 100644 3 lab/build.gradle create mode 100644 3 lab/gradle/wrapper/gradle-wrapper.jar create mode 100644 3 lab/gradle/wrapper/gradle-wrapper.properties create mode 100644 3 lab/gradlew create mode 100644 3 lab/gradlew.bat create mode 100644 3 lab/readme.md create mode 100644 3 lab/settings.gradle create mode 100644 3 lab/src/main/java/com/example/demo/DemoApplication.java create mode 100644 3 lab/src/main/java/com/example/demo/comments/api/CommentController.java create mode 100644 3 lab/src/main/java/com/example/demo/comments/api/CommentDto.java create mode 100644 3 lab/src/main/java/com/example/demo/comments/model/CommentEntity.java create mode 100644 3 lab/src/main/java/com/example/demo/comments/repository/CommentRepository.java create mode 100644 3 lab/src/main/java/com/example/demo/comments/service/CommentService.java create mode 100644 3 lab/src/main/java/com/example/demo/core/api/PageDto.java create mode 100644 3 lab/src/main/java/com/example/demo/core/api/PageDtoMapper.java create mode 100644 3 lab/src/main/java/com/example/demo/core/configuration/Constants.java create mode 100644 3 lab/src/main/java/com/example/demo/core/configuration/MapperConfiguration.java create mode 100644 3 lab/src/main/java/com/example/demo/core/configuration/WebConfiguration.java create mode 100644 3 lab/src/main/java/com/example/demo/core/convert.java create mode 100644 3 lab/src/main/java/com/example/demo/core/error/NotFoundException.java create mode 100644 3 lab/src/main/java/com/example/demo/core/model/BaseEntity.java create mode 100644 3 lab/src/main/java/com/example/demo/messages/api/MessageController.java create mode 100644 3 lab/src/main/java/com/example/demo/messages/api/MessageDto.java create mode 100644 3 lab/src/main/java/com/example/demo/messages/model/MessageEntity.java create mode 100644 3 lab/src/main/java/com/example/demo/messages/repository/MessageRepository.java create mode 100644 3 lab/src/main/java/com/example/demo/messages/service/MessageService.java create mode 100644 3 lab/src/main/java/com/example/demo/posts/api/PostController.java create mode 100644 3 lab/src/main/java/com/example/demo/posts/api/PostDto.java create mode 100644 3 lab/src/main/java/com/example/demo/posts/model/PostEntity.java create mode 100644 3 lab/src/main/java/com/example/demo/posts/repository/PostRepository.java create mode 100644 3 lab/src/main/java/com/example/demo/posts/service/PostService.java create mode 100644 3 lab/src/main/java/com/example/demo/users/api/UserController.java create mode 100644 3 lab/src/main/java/com/example/demo/users/api/UserDto.java create mode 100644 3 lab/src/main/java/com/example/demo/users/api/UserTopDto.java create mode 100644 3 lab/src/main/java/com/example/demo/users/model/UserEntity.java create mode 100644 3 lab/src/main/java/com/example/demo/users/model/UserTop.java create mode 100644 3 lab/src/main/java/com/example/demo/users/repository/UserRepository.java create mode 100644 3 lab/src/main/java/com/example/demo/users/service/UserService.java create mode 100644 3 lab/src/main/resources/application.properties create mode 100644 3 lab/src/test/java/com/example/demo/CommentServiceTests.java create mode 100644 3 lab/src/test/java/com/example/demo/MessageServiceTests.java create mode 100644 3 lab/src/test/java/com/example/demo/PostServiceTests.java create mode 100644 3 lab/src/test/java/com/example/demo/UserServiceTests.java create mode 100644 3 lab/src/test/resources/application.properties create mode 100644 5 lab/.gitignore create mode 100644 5 lab/.vscode/extensions.json create mode 100644 5 lab/.vscode/launch.json create mode 100644 5 lab/.vscode/settings.json create mode 100644 5 lab/build.gradle create mode 100644 5 lab/gradle/wrapper/gradle-wrapper.jar create mode 100644 5 lab/gradle/wrapper/gradle-wrapper.properties create mode 100644 5 lab/gradlew create mode 100644 5 lab/gradlew.bat create mode 100644 5 lab/readme.md create mode 100644 5 lab/settings.gradle create mode 100644 5 lab/src/main/java/com/example/demo/DemoApplication.java create mode 100644 5 lab/src/main/java/com/example/demo/comments/api/CommentController.java create mode 100644 5 lab/src/main/java/com/example/demo/comments/api/CommentDto.java create mode 100644 5 lab/src/main/java/com/example/demo/comments/api/PostCommentDto.java create mode 100644 5 lab/src/main/java/com/example/demo/comments/model/CommentEntity.java create mode 100644 5 lab/src/main/java/com/example/demo/comments/repository/CommentRepository.java create mode 100644 5 lab/src/main/java/com/example/demo/comments/service/CommentService.java create mode 100644 5 lab/src/main/java/com/example/demo/core/api/GlobalController.java create mode 100644 5 lab/src/main/java/com/example/demo/core/api/PageAttributesMapper.java create mode 100644 5 lab/src/main/java/com/example/demo/core/configuration/Constants.java create mode 100644 5 lab/src/main/java/com/example/demo/core/configuration/MapperConfiguration.java create mode 100644 5 lab/src/main/java/com/example/demo/core/configuration/WebConfiguration.java create mode 100644 5 lab/src/main/java/com/example/demo/core/convert.java create mode 100644 5 lab/src/main/java/com/example/demo/core/error/AdviceController.java create mode 100644 5 lab/src/main/java/com/example/demo/core/error/NotFoundException.java create mode 100644 5 lab/src/main/java/com/example/demo/core/model/BaseEntity.java create mode 100644 5 lab/src/main/java/com/example/demo/core/security/SecurityConfiguration.java create mode 100644 5 lab/src/main/java/com/example/demo/core/security/UserPrincipal.java create mode 100644 5 lab/src/main/java/com/example/demo/messages/api/DialogController.java create mode 100644 5 lab/src/main/java/com/example/demo/messages/api/DialogDto.java create mode 100644 5 lab/src/main/java/com/example/demo/messages/api/MessageController.java create mode 100644 5 lab/src/main/java/com/example/demo/messages/api/MessageDto.java create mode 100644 5 lab/src/main/java/com/example/demo/messages/model/MessageEntity.java create mode 100644 5 lab/src/main/java/com/example/demo/messages/repository/MessageRepository.java create mode 100644 5 lab/src/main/java/com/example/demo/messages/service/MessageService.java create mode 100644 5 lab/src/main/java/com/example/demo/posts/api/HomeController.java create mode 100644 5 lab/src/main/java/com/example/demo/posts/api/HomePostDto.java create mode 100644 5 lab/src/main/java/com/example/demo/posts/api/PostController.java create mode 100644 5 lab/src/main/java/com/example/demo/posts/api/PostDto.java create mode 100644 5 lab/src/main/java/com/example/demo/posts/model/PostEntity.java create mode 100644 5 lab/src/main/java/com/example/demo/posts/repository/PostRepository.java create mode 100644 5 lab/src/main/java/com/example/demo/posts/service/PostService.java create mode 100644 5 lab/src/main/java/com/example/demo/users/api/FriendDto.java create mode 100644 5 lab/src/main/java/com/example/demo/users/api/UserController.java create mode 100644 5 lab/src/main/java/com/example/demo/users/api/UserDto.java create mode 100644 5 lab/src/main/java/com/example/demo/users/api/UserProfileController.java create mode 100644 5 lab/src/main/java/com/example/demo/users/api/UserProfileDto.java create mode 100644 5 lab/src/main/java/com/example/demo/users/api/UserSignupController.java create mode 100644 5 lab/src/main/java/com/example/demo/users/api/UserSignupDto.java create mode 100644 5 lab/src/main/java/com/example/demo/users/api/UserTopDto.java create mode 100644 5 lab/src/main/java/com/example/demo/users/model/FriendsEntity.java create mode 100644 5 lab/src/main/java/com/example/demo/users/model/UserEntity.java create mode 100644 5 lab/src/main/java/com/example/demo/users/model/UserRole.java create mode 100644 5 lab/src/main/java/com/example/demo/users/model/UserTop.java create mode 100644 5 lab/src/main/java/com/example/demo/users/repository/UserRepository.java create mode 100644 5 lab/src/main/java/com/example/demo/users/service/Pageable.java create mode 100644 5 lab/src/main/java/com/example/demo/users/service/UserService.java create mode 100644 5 lab/src/main/resources/application.properties create mode 100644 5 lab/src/main/resources/public/css/style.css create mode 100644 5 lab/src/main/resources/public/eye.png create mode 100644 5 lab/src/main/resources/public/favicon.svg create mode 100644 5 lab/src/main/resources/public/like.png create mode 100644 5 lab/src/main/resources/templates.zip create mode 100644 5 lab/src/main/resources/templates/comment-edit.html create mode 100644 5 lab/src/main/resources/templates/comment.html create mode 100644 5 lab/src/main/resources/templates/comments.html create mode 100644 5 lab/src/main/resources/templates/default.html create mode 100644 5 lab/src/main/resources/templates/dialog.html create mode 100644 5 lab/src/main/resources/templates/dialogs.html create mode 100644 5 lab/src/main/resources/templates/error.html create mode 100644 5 lab/src/main/resources/templates/friends.html create mode 100644 5 lab/src/main/resources/templates/home.html create mode 100644 5 lab/src/main/resources/templates/login.html create mode 100644 5 lab/src/main/resources/templates/message.html create mode 100644 5 lab/src/main/resources/templates/pagination.html create mode 100644 5 lab/src/main/resources/templates/post-edit.html create mode 100644 5 lab/src/main/resources/templates/post.html create mode 100644 5 lab/src/main/resources/templates/profile.html create mode 100644 5 lab/src/main/resources/templates/signup.html create mode 100644 5 lab/src/main/resources/templates/user-edit.html create mode 100644 5 lab/src/main/resources/templates/user.html create mode 100644 5 lab/src/test/resources/application.properties diff --git a/1 lab/.gitignore b/1 lab/.gitignore new file mode 100644 index 0000000..c2065bc --- /dev/null +++ b/1 lab/.gitignore @@ -0,0 +1,37 @@ +HELP.md +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ diff --git a/1 lab/build.gradle b/1 lab/build.gradle new file mode 100644 index 0000000..2263922 --- /dev/null +++ b/1 lab/build.gradle @@ -0,0 +1,27 @@ +plugins { + id 'java' + id 'org.springframework.boot' version '3.2.2' + id 'io.spring.dependency-management' version '1.1.4' +} + +group = 'com.example' +version = '0.0.1-SNAPSHOT' + +java { + sourceCompatibility = '17' +} + +repositories { + mavenCentral() +} + +dependencies { + implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.3.0' + + testImplementation 'org.springframework.boot:spring-boot-starter-test' +} + +tasks.named('test') { + useJUnitPlatform() +} diff --git a/1 lab/gradle/wrapper/gradle-wrapper.jar b/1 lab/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..d64cd4917707c1f8861d8cb53dd15194d4248596 GIT binary patch literal 43462 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 literal 0 HcmV?d00001 diff --git a/1 lab/gradle/wrapper/gradle-wrapper.properties b/1 lab/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..1af9e09 --- /dev/null +++ b/1 lab/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/1 lab/gradlew b/1 lab/gradlew new file mode 100644 index 0000000..1aa94a4 --- /dev/null +++ b/1 lab/gradlew @@ -0,0 +1,249 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/1 lab/gradlew.bat b/1 lab/gradlew.bat new file mode 100644 index 0000000..93e3f59 --- /dev/null +++ b/1 lab/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/1 lab/index.html b/1 lab/index.html new file mode 100644 index 0000000..d0360bb --- /dev/null +++ b/1 lab/index.html @@ -0,0 +1,64 @@ + + + + + + + Document + + + +

Push the button

+ +
+ +
+ +
+ +
+ + + + + \ No newline at end of file diff --git a/1 lab/settings.gradle b/1 lab/settings.gradle new file mode 100644 index 0000000..0a383dd --- /dev/null +++ b/1 lab/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'demo' diff --git a/1 lab/src/main/java/com/example/demo/ApiController.java b/1 lab/src/main/java/com/example/demo/ApiController.java new file mode 100644 index 0000000..1de6728 --- /dev/null +++ b/1 lab/src/main/java/com/example/demo/ApiController.java @@ -0,0 +1,54 @@ +package com.example.demo; + +import java.util.ArrayList; +import java.util.List; + +//import org.slf4j.Logger; +//import org.slf4j.LoggerFactory; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import jakarta.validation.Valid; + +@RestController +@RequestMapping("/message") +public class ApiController { + // private final Logger log = LoggerFactory.getLogger(ApiController.class); + + public List dataList = new ArrayList(); + + @PostMapping + public UserDto create(@RequestBody @Valid UserDto testDto) { + dataList.add(testDto); + return testDto; + } + + @GetMapping + public List readAll() { + return dataList; + } + + @GetMapping("{id}") + public UserDto read(@PathVariable(name = "id") int id) { + return dataList.get(id); + } + + @PutMapping("/{id}") + public UserDto update(@PathVariable Integer id, @RequestBody UserDto testDto) { + dataList.set(id, testDto); + return testDto; + } + + @DeleteMapping("/{id}") + public UserDto delete(@PathVariable(name = "id") int id) { + UserDto temp = dataList.remove(id); + return temp; + } + +} diff --git a/1 lab/src/main/java/com/example/demo/DemoApplication.java b/1 lab/src/main/java/com/example/demo/DemoApplication.java new file mode 100644 index 0000000..e03ec75 --- /dev/null +++ b/1 lab/src/main/java/com/example/demo/DemoApplication.java @@ -0,0 +1,12 @@ +package com.example.demo; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class DemoApplication { + + public static void main(String[] args) { + SpringApplication.run(DemoApplication.class, args); + } +} diff --git a/1 lab/src/main/java/com/example/demo/UserDto.java b/1 lab/src/main/java/com/example/demo/UserDto.java new file mode 100644 index 0000000..bd32688 --- /dev/null +++ b/1 lab/src/main/java/com/example/demo/UserDto.java @@ -0,0 +1,43 @@ +package com.example.demo; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +public class UserDto { + private Integer groupId; + private Integer senderId; + private String msg; + private String time; + + public UserDto() { + } + + @JsonCreator + public UserDto( + @JsonProperty(value = "groupId") Integer groupId, + @JsonProperty(value = "senderId") Integer senderId, + @JsonProperty(value = "msg") String msg, + @JsonProperty(value = "time") String time) { + this.groupId = groupId; + this.senderId = senderId; + this.msg = msg; + this.time = time; + } + + public Integer getGroupId() { + return groupId; + } + + public Integer getSenderId() { + return senderId; + } + + public String getMsg() { + return msg; + } + + public String getTime() { + return time; + } + +} diff --git a/1 lab/src/main/java/com/example/demo/WebConfig.java b/1 lab/src/main/java/com/example/demo/WebConfig.java new file mode 100644 index 0000000..d5585a1 --- /dev/null +++ b/1 lab/src/main/java/com/example/demo/WebConfig.java @@ -0,0 +1,15 @@ +package com.example.demo; + +import org.springframework.context.annotation.Configuration; +import org.springframework.lang.NonNull; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +public class WebConfig implements WebMvcConfigurer { + @Override + public void addCorsMappings(@NonNull CorsRegistry registry) { + registry.addMapping("/**") + .allowedMethods("GET", "POST", "PUT", "DELETE"); + } +} diff --git a/1 lab/src/main/resources/application.properties b/1 lab/src/main/resources/application.properties new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/1 lab/src/main/resources/application.properties @@ -0,0 +1 @@ + diff --git a/1 lab/src/test/java/com/example/demo/DemoApplicationTests.java b/1 lab/src/test/java/com/example/demo/DemoApplicationTests.java new file mode 100644 index 0000000..2778a6a --- /dev/null +++ b/1 lab/src/test/java/com/example/demo/DemoApplicationTests.java @@ -0,0 +1,13 @@ +package com.example.demo; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class DemoApplicationTests { + + @Test + void contextLoads() { + } + +} diff --git a/2 lab/.gitignore b/2 lab/.gitignore new file mode 100644 index 0000000..c2065bc --- /dev/null +++ b/2 lab/.gitignore @@ -0,0 +1,37 @@ +HELP.md +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ diff --git a/2 lab/.vs/IP/FileContentIndex/c3f4083e-43c8-4e91-bcd1-849b3c786de3.vsidx b/2 lab/.vs/IP/FileContentIndex/c3f4083e-43c8-4e91-bcd1-849b3c786de3.vsidx new file mode 100644 index 0000000000000000000000000000000000000000..076106fd650768e9c2deb84726e167879ab53af3 GIT binary patch literal 96427 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 z z=intV+hs1ue0xbQf$SFxWIXdh`r8NOepUk+M+0Pe7vy{Kw@W@;JW@PLTn{piqe1rb zJEcDnq`gzb_lReL?Dvm>^mCqgzIXx1I6n!p{KX*013zb*aklJAmyk9eQ> zU6AoTDET3f^Y+Ie{XZ`LMEohpc%P8`bMZ;>7a;BZ3S=C=mHa;-$LU`{+WjlY_W76e zTjP*HyEDb@#MgtgGfVOtLALkqlHUe)Lau_e?}E(VQhET=Unnk@K9-zF9uddH6(HlA zkh}_H`>p}!fgb?r=WL~)1G3)disys$`w5V7eM<5rAnW-Vkn{hm(qAF{l_33o1El@y zLDu(nrGH2IdnDfna@>4h{1HgMkAf`!I5-!aj>9PD?xi zD18Z}T?=G8>@9g8r4NYvfsDt1wBL~2k{pQ3LE33c-XCOK6XL%u|z8}Prl)FUfpAkPNUIsFbFM=$0xp<}0uM)2TneUq*{a+{j4ItyYMe?`B+r{sI z{GRm*kn`g=Anp7Pw+r9T3)eosjMB*^l=ko-&W*C6fvPW+?v zf0F!H$$uCB33C3w0e=X|_Lv2-{LbR8ApN`r#F05?fy~z@E&$mti^YAV?-!R!zaL1u zHL)QMgS6KY1CaZ{3d!T*fgsBtB>52WP>}H*1~QJr#kJBOC9VTmexu|~ARcvdr-Sr= z2FUqzzT^u)j2H>Nt9TnoKX;1vN`JrP2PHoQa$NohWc-hc|0O;K zGOowPpGyBTkp1;X$xnlf^KT%>#|-=u2gm&zK-%3woCUIdb^>X47m#tjMe=Tv_mn(G zoC`9}g5(~t7o`0XNV|QK=Ywp=8c07z`nu$%I3#)?;|L{3Vj_+xeFexkCP3DAwbBm* z*^ehkf0Fd4fQ;+i(w_y=&j%!*Bl$cq5B;?uje<#R!`(5#2koJEB((aEz zmj4NeNB7*HK(@~_Ajk7GY?hR_5~qVKHxp#J?LgLV2kCbb-wd)pXM@=}N&2$55M=qq zAj|hlUJCMiieYhu^yA`z;!5!#@eq*y)=FL{zC+w79xWaVa@?H^vR_UIS+D`j!}gY|ljU2*`PNC`f;YDg8*0^;|D*Qu@&#<2Y74 z9%Mb<39^2tNPim0^6v#1--kiwI|pRG^Fi9V0A#$M0l6N1P5Q5kSBu{OY43W;H-a3u zw}FiNc98b(lzf+XH%LGCiua2Th!27+|9$Zx@drx(q4*=P6ZvwpC+$rG8PC?@b|Bvu zy%}VG?g`TFY;g|A_&OzbiSt0VN3Z0PXo+R952W3FLH1t-q<m-o*PZi$-((jqzJn$z8Pe`w@BVY@}3~ur5ntHE=W5m z$Z~Cv<@Og35GTY{AoHyTJHSIhj@R`d%WVLeevIVf#1o`H5#;0kXX=1R4M5 zK-#$sq@6E`UlqTu^s7O(_x0i}Ap7MGrQa#uE#9m2?|~i2_n`QF@geaC;=>^QKPvgZ zK>GWc@?{zKdie+P+&O1}o22K^Br z%deNb0p$7iy^_xax!;^8`FxP}E(GcCvmncVLFr!-zXCGf*Fd({H$cX7o#dNAj?;TV zj*}mN%>N^h->flzO3o?!vr2iCT`8J3y&z&IsJ3-d}WTn3wWZdT~{Q}8XNWN0M3S_@uE%_Ug zua$hAV#bGPLC#Royg{gC*u_y|ZlkBd)&wDT+Rx8fhfXF&E3 z7k}ED2C}}}NZwZR8^Gz%zY%0SJ4wGQ$ntZ<9&jtjC2%@8A7uUoAmiFsTn5tLAjteR zkog?ZfE*7kkoHGF#oE+z5=A3E5)nCYe3rhhU9NbzD~Se`WwZY!0E{UZII*l zZjj~g5$_f61L^O(k{^)#Ajo$3f#e^GKLWG)2h#3u!0F)cLE8Tx$$tV_{x6dM0n*RE zK(^zIuF3pcgPiAY1lg`{23hW{Annfq*^foZeIWhJ2WfAixEP!cd5Po!u_Ep#+F}i4 z{)RX#eM=0)CS4bWg4+I(aYRQL*Ye42d5~Sbt;0*9+$;X4N&t}Q*5>FLR zSNa*^S>pSY{vnXWJ_^$QY=F8$ji=f!#8*2vcjay;$}vY!1Q{SAQJPh*hwMwQ-H`U=V8AlIAK zAoHz}{s?g`$Z@b<>6<`~m(5DwqV$u*Q^Bo~eg?=m&J@oA8Q=Rr`uULLvz7i4knQ(L zkmKSrAlIqOLHhq1I0L)}q}^|TJfGht{aqmK-viSAcO`#M@((2c5X2){?$03oP3xYt zw-w0pGeG*ADS12b_2L^q`riR$JUdJOCUIBst>W$=^Y0;fPsy_-=fzHOo>&ljK$hQ2 z>;+lBKFRaN1t8;JBY;k^V%G`A-Ac{%0utEb)CH+w1+3 zKPdS_lFt^;5kIE%bH($dKVQ5+ybz?{PfPx+cqz!ZzX)<3UIQ|&>y>^Z$a3G7d^?D* zBy*2}tlv*S#`{yrKbQO@$a+5o(%yed{vF76`!mRLe*tOd?;z*#jKXBOZNzOs`rkqF zjv&YVZXnC=4l@4PAnnXmdI_Yxy+OvYFUWJC4|4ppL6%p+vBq!^Ia-_9%TGqlKd6%t041V18~ic zN8Af!yp}j0q`igWVvu(BR{9d@2gHiFA4vZ-kog_y>ma@|%S}jLrSyX&A0i$KvVD#K z8Rt8s-w4ixyjl8_LB4S@oAh^pjPD+h_U;w$1DXH(ApQRUWWGn0{$C*Ddjh1rr$FZWmG~Qw`{RtBN&PnB zwjlFwFYYM)PU0@&o5AgnZw|=%6hYR%SMnl|=jJNNxEdhG(HO`$R*2&u{jCI9zg5y7 zEcp;|wRos_nD};(<&Ojz$0m^Wj{*4}<7~<2h#wQr0~zlpK>EEv`U}O2#7`;x63L$t zKPO%W((hM6+POmdE5)lo#&@mc>%<$RzZqov+zv9HJEgxDWIgT|9{{<&J_fR1e-6^$ zlS+R|{1r$$|1J4<;{S+$6#oP=?tg-`H)F5KIJX7q_jMrcyg~Xmif>Z-t{}_L2AOZJ z(z}&j1ZlTV`b8keYroQ$O0I%jSI4AZ0kU5EOFlrHkbb4O3fvC)jsWTZDCyUMjQ<_t zMx`I4^kbzzUh)Z&-zj;EHi|}Qy}YeiR90S zp99$*Ujk|8o6=t|-YDKA-U9O6|9z0{{$C*5?T;Yi`!h(te--~O{!`5LPUf2~&II`$ z=*=L@y#;(d*d@6eWZVVGJs|y9lKaF3((euO`yE&MVX>w3{G0c5_7l8*)%@9~m1gPdn4Nj?SS_ZFW3S?&UmaeNwNxl2K| z%jd-}N`JX{rSw;c*GPYze#ZCqRzZ-%9?y_y>^wo{{_)@oymG&6Ot8r-N*d zZ6v==+yP{qJBmAryD0rFl6M1H&)vm6K-RZQ>D`jcO7D|?f#ikaB5+%jt4Ll3(*K}X zmA(ct-n!^YKO_!|9?1HKAninA0&;$=Q2K!&?X3c{c@HxGT5%o7`mG0PZ8FUNil-_4J(5oc>F+G*-v@G^`=s<2OMi*@8S!)CW#Sh=#{Wg} zOVWQu^5x=JrT?1vb@6KP8{#*`>%|+zo5Wi{`ny%~ZQ>o`o#Ndf>vzB82gC=(hs1}) zM?m)5;~>jDAwH$_Ux~kw{`ZpqAU+MUpZ_fRuj1c9#`_QPpVH^7$#F1EoGxxH&J?!; zX?J_cJAkw|OY$2f?<9E_kok9&{8pv!ChjioA-+xAQ=AQQeCEYYah_NZd&D3A9GUSS z=X?P_gJoQHq=kp^lY5xoX?CzdJ2-1Qn82UhG>W^j)F6l5AoERQzG=MMm3O=H?n>TW z$-9_${ByY6dfu()-AdlA zvfM1*&EVaP=ck6$h1A)+v$Oi*_abs1W~Hb1;c^bemCV9FXq$9-607<+y`9vJ%dEM$ zI9<5dEauF^g}>#So3%YJG%%9}CSzfwlkGxX2aR@6H%NPfQ@W`;nwmya(|GQm&coz% zt~}Fla?buajog1y_g0GhEse?gPSxDd2mg7R+<)>i^!!*R+b^pE!#F9cR#xdQi2U{< zH=WfQoT?G6%;cFpt9dp+vaaZ4+0M+-ROZ)1*FjU6m02{^!4}GfPgaku8S{G>y~(`g zaW|boKBvXAsAy`C=J_2@ZWixWX0c3pqd09!d^UO~Gs+5XM^oF;6yGf6W-&B7>#{!F zO=m(jVkg@rYfc)?yq%Oa1Vft~FHFdES@roZWahSE&t!vta*}XvEWl500gFvd4TqT> z2Gqy9KHg1Dmnq$3e>^|+#U@jORvOF~@h;-sRN2YOP`8q)*#w)-yKFKI@-B;c>h8DH z{gx#@!aM#oY4%TK<}T;mRK6)IQ&TcziEP47`JJ4cEIWs(b9gsdUpC^66i;G8Hn)CE zu}ZPZyGb8Y?Z>;xK|NLM`KeRwJZY4vlcR}uQ`3HGlJL(?b2~DXe{h=H`T2LtsayX1 zvMF`^*B+m{coZOJEjiVfgRIA3HeHx`%H`DEWVlS73Vf>RrW$tA6e~SBxOg|Eo6NTr zbhEjk%;w!R-c94(AnyjJ?%3=rDNbj?^i1S=s)N52ldVWo_NJ`NWabyFX0utr%;gB} z-9%F}d6`bJBP+p3r|NOivpxNs4Woc8gV|!qDRlAFlE&#gx&CF#(p-p6)|^xGobz%= zy8IR|e5L!`iOp0eo0n4o&4G?b*~tavvKOr5*?PbT2KfrAgPHkzrP;&jWU*O&5dY59 zlnJx4=<$e?+nL4q%N9AC34_@p&CJK~l%It$n?c^pvV+fNp2cUVSu7ED;xe7J+>!S9 zo+~@l<~nF4yJX8v2j8AAXG>S?NCV4R z%sk)QlPodo*}k+H{bZZ+{an_ZS!k2p%@H;&>p>bFWTmsHc3~J(LwWL!0cG15-^%3- zH=7RD{4uuf$2k77!$j6rnU24$m9rVkAkEKUzLTabQ=H8#DN}c5zFDkM$Ft*funXe! ztO}X8Y)gF|v(WEW+5E`5hX!V4(KE|N!`+#?=`gD`NXvuF^5bW#FnMIkrWzA2qqm!~5s@_= z#h(8BoS(Yi?)Y39_#~pj}sLQ)c z@RPfQcjxhr|C)NP$-CxMK31@g73A-*zwlH#S#xHYTG6LAhbi4uz7d)lVH;%Ad1^m@ z;i%(=G1ZNe6O4|g_SIS2Kpf=jkin@tE`m39;Nq|fbf#l1Z%>-qqn;o0)Q)pfmXE2E zF+ab=RNW>^&n{-ac*|eJU#uBqx z^Vw6;Q+E?}b>5xCyUES)`Q|6veyXZWtuk{p^TAs3CpT>>%ajl5UU<@H>SXUvrLxbn zt#_(%rtYSO%496r!$9Wz*+}2WyU7aD6#t3&7e5OyVUW@D{}wWNDwu5XXH89;8ZR_e zeg4Xy9kD0(WX|8`JR5M4`4=pCafzIkwR09r=8a2QwoqjIM+alh_TLU>$({`|Q?s+WW!0LBo(*;*Lv?7m%5XFD zT`Zezz1br&`zbe*22NtW$!(FQCjCCUCQdHvj6QQdJzLfpWM{TVvClhrj>y_2bHv{Z z&)GDUoeIv)Dl>;!vbN{i*)f|Ju1z~W+ZEaTIG3%KJ(5lC5WURDu=?2^^#HB#x56h61UBt0XUrYX z#%yOXcT&d|+L2?(W)%zr&z`nBdT^KZ`HpO>9a#nY*(zk)gkcprScMLbuntDb-^R#w zJZm}Iw>qA+JeZ}jsx#S~{FT?qeLCBTC*$J=lx^z#L&97~R$tb9vU^zR$%FL^D$Svs z@X3LHuaHas|5X14K~7emBXM%vXE8s&`nxd57p*=g*dTs#gNz^>0ybNczXg-C*-VoY zF{}Brhl%WT_@S&_IH36_AG!3|7RpY@yS!j1v!k-^$Qiir8yW(Grh30ZI zr`8-c)8zBfEZm*Kd439`nniEfk%@mdnj2(U=9oO1W~a1F#{!c_)0H&+wr8Vy&Y^>4 zXX7U~n>FI!pX8Q5+aDd-aGVXD&3q@}CwCI>`19OVK5IXQFi4+S1hcaC%M!9S%|woa z!T(p>`^Q+8miL|a_Ea(5!wgf!c3Wd7#;1F#plCy=L|Mmnc9nD6RitMbbFK$p*UvAcsyN!)?kWvqTOcl2$NU9yoXp}E#vmkRJ#_fRfUQW z(8UR$lP+nnLRX;%m1T-i_{zMf45A>$&_N!jg|niUL)hbB8GFU8A)*>3i3fR1Aw|sC zX_$$QOpzLU(4f}>)62){R3%e+De-LOc{{~V`U+T zC<`u}zD)OfN+O+(QKnT|jOjjrgC%DPjIQ0m1*S})q`rqkZAV874OS|#5A{^UX)BDT z2?y`Qr2r!|D)C9^>`ZflT6~e7RoKig ztp`bfHum5;RW3r#ik8CZc@sH>iqp~xx}VZbUQ~(hztH|hUntF+e;bA6*+X>tkTS%> zqm_<&#|=oUD@ql!28>yiumV^x8>15-Q%Zab{oO(bsmI{_e%a_+`x{;3_M`LYzn>QG zr%KoUMjeFUM5xid^b$OH%im}a+w%7FwEaAtcJo7aVqVWE1rM0AkfJ_IFK4N;MQMxv z4(8D+WLAI!2R8c7>YmY#(ag6G@b&@T2Dhq5+w;6VPvw68#grE^zD65}1(jb7e6OaL z$U${bCEh;P{zi{6M#n|rAB@J^W7IQU-qUHn3}4WC7=mSif-yFzyuq_IC4%mSIr4%s z{SdaP8*}0I1M|1)zDWr_yB+;3?B)Qp#NO8cqcW*ABVASsLxF1Xtw^KxI*}FBd5_VK z7c2!m&laapR94msG-vb~h`0LRp0+)xI*=*Wg%r(I2zJ$avw<|t;}?#qDA26pt8~BJ zp6NOy?CrCGDt#>aia^n7k$&`YH<#nc25=`1)3cA&C?HrG#L!F`S~gkEkY(ZMMH<-i zwY7E|Sa^|VV7b^8Z$;4A)6`oyJxGjs>60lLRk;?Z>9bzNz~in}iG!rAmOrBbq#ir&7`fU2 zqh-syL3#$p!;~CN=x^gT%X@S%VREK-)*>aX1V$zD{6yX8^*qaBBBh8PU8x`ch${bx z{)+as-czLr1NCazX%7foK8yIwcds#zL7`c#gB5!O{Ov&Sc0`Yp3rCmcg|Mt+nlVNk z2BlK9HQKK6?Bn#ukF1Pt=h^KHzGy+Hca3`2;72F-c^3NuaZIf}Ty1$K&t2$SAY$oJ zN2eXJ-gui9rU~wDB~C%hGW5!3D;ZvPgqQm6De{~IHbX`Z0PT<$duMotAg$J0V#2I& z5KsOvw@wOo&$iN-kg2D8E%jf+6qu%Wf#Tw;s3I6dea_2H(c(BQ3>FtwR-BV`v`B1A z$(5B3_>Kdb)4m@9zN|O$;uT%HtN{HfVpbiqIg0TXx3A?w@NO8dg_Cs72v!K4orCQ_ z$-fLW`Wg*xg$tA-8$jiHXz~`cc(Fk*17+?v!|l$(jUhQ!Vf0?Su7k5Hd9=$!A`?$y zCg%oPBy(S@$cEERjv$Juy0$?*C9%8^1C{apDc=4R{VnEhq!9EoP31rw`jEJNotfbn z_{^_Wu1YI4X58^i4$LTNxY;#-W9qBaAx$Q%V#AOZB~fdpm{r2+hhUUod#g@U)uP6KwDX0-V(lz&0nE;l`CDwH%*m%i$(!o+1=RK!k1DR z^SgnCpI*K(OPGnH{K$3$u>2}SoNTm4)U@CZqC(;y3f+gT&~F)N%ySo1hleI3Ct(J zvWm2?A?-=26h@eT_d-NJkT5ZS7kKm#_Fd4$W&Er@(Bc5JocZflCwfW7&AKVgCTH3u zi>EV469oZP?^_u~$rRit!SedJUG7Cx2I~+IEb8aDK>tnJa*n|;pL3$!CVdA4-vl7d z*JP`eE`Wzgol_8Yg7CXoauI+oLUw88wzo>}{9nwEjQNulSDi}^=JnV0faghw1HECz zI+D0xXa|b|pwK~_btPh^u$A69IyJ5wfV6t9IgD@-YGhqV8h}S;e2{oxco^InhGE4i ztl235zs`&6P;wA&z!WEi{I2ro4#rLH5UI-wn<&U%M1HWYCN%(e0=KmHI{Ka#&%MHS z=2)w8t^JLhI4P`Vr}Yq%?e?OBd3)ei=Kq(F9I3(QM0uNt8(4lG6n>s6tGrdRjf|Yr zRWDbmr?=8~uR4nc!D~fRWW%^53w?AS8v*@do8YfbQ*b}m?ncQ+(A6KI3(o0oMv<{k5h@sW0 zXxw7|K%wRkDWUsXfiQq7Rr)+otpf{ZSd32ccFOG(=*yEl%Ckqo(itf~U@>}vXHP(g zh4|_h>GZw4{Y7}ROXbVlUZxaCX?8Hr_@}AzG!zotC$K%yrqP71)NXaFCMLlMAYJe< zNx5bO1-C#bk@+>~R|4{#44x?&WBiXGlZZkvu$^w{bFpNlYqyaIeB&OlaE!+dy+mS9 zAx&f0*v|q7sbS!6B$xmf33Hipioh8{WH}vC(a>gfX}jG%0Qh$n{pQ!{eh?Jau|AUN zwLrxo;8~Tqq;G6@asHny4~vhwPN$r_HM)qr{40>&2Whuii6GzaX6AKqvz6S6q-WAE z7?1BDVLA-Mqutxv?P~#?-=-g#=j!3Y`Sv$Dudo8f;`~yH1)vyE#TGnPX>olfe{Y6^ zzdKX$zXJLRC29LLklCjzvk>a}DM<1)7_|!u?jdmbLIb)Ew<_uWb@=f#D7Z3uhSCl_ zm!2z_>-u;GFU$WF<0^?y(W#o;F?a{eCX<#X@{k;j+ZJZUyyN1U0|V8}Q02T1w+2fP z40IiJQP#2!0o4+Wfx;G5wkSniDBa7mdyz2X#28;*{4_6qn$oXP`n8RAF>3B1qN`fY zHH_e$r5XVo(Bgm=nwEql^JID*Z(ql=(!8g5_7qYADACo$H4%DFiXZLC>IlyY=1cT) zX^DNQT+4kbHd-bd?}K1|_CV8-+aNL}1D05MO4oRM4X@!g7Xx7f5u=CQ?e?Mrmav;r zY$v6A==vT?B|~@d%ng&Vi7={XxL#>zNEBf7YBVJ~yC7IO*ISfvdb#CHyF`G&&9!%p zrPCHCMCz)!((_a?iLe9aE?CO91^@90wNYiF#V$rcq4|V*Nn}(r^>x(rq_Z}l{~wU{ zRrKRlI^d+o(G#;?IEZ_6&LFF6CGF|OjaFL2(ij}CBVn3SV5PXod3YpI7Y zOwd~QGyGfVToO@S2<@SBqB)vfqS+;2-bd{I43N5fagut6@WcH+k78~vgV82<7(q!> z4fiTThVk%i+Wv-1GrXO`sHy(Q%E+Gpb#S$Od`CT>RiK%+^!#}H^9Py+xtp{=O5Q^3 z^A`FmxA?1|^;J5xbh^;55A!5;r(l7J&mysrZ1#A$~#op7Y<|YcZ)oz`ag(QgH>RFl8^t-)?HPkDQWdOkx++`BMA7m`vgz^UTSsc&G`cWD-h-EO7py!~Z5xWKr3 zGp$$$de{6XwAiC(6>LJYJt!XYz(7HeQtUWyW96Z4+yrlb9s2z`L+W-)q}MCzn=S85{{m-fL&*?8H%!B(D-tzls z1GMTxgo6GG^{!B_r_=)h$D}`Bh7YnYZ2!LLu7rOmz8GBHB41b@lDh5vA07*>a zP9Vh5TMmKrI=*KNH73wykYa${W)~`rJHc~0BVCqVFpc{rIJkdXyF8BhItIA6%@i@_ z3sf&bus;S;w|2UZ)^PQbU;vPjmf@;X{fVN7d2lz7;p*36fGS=+p8>5i`eb0qUNAJ^ zxih5evuTS68bxYA_hI)Q1N9KQq;4PPX?LH5&RV1=8P`b0phJG#2EH1ee8T~P7Xi|B zjgzl!B^|gX=UiuU5Qc0{Hk(#!@(NuyVIlI(5K6A6CUMl8_*@Qid zxpl@ZOJlt27QE!v1s4lv`Xo^wTs`k-5&|0RQ13WWatgo)y3g$4y#Ov*_bLiuYBAAg-+3_;Y_v%JLTDK z&&;VFtG1F;`Ga&W-<}5F3(iQ9$vxSqm>~jYb+@5QPd)cmKyZse-`j)Y0M=XYl=H++DKHGe!JKz>S=WFSx1f ztJ|$~m58E8A(U}<$>)}F6JSs!dA$OBlNJzL6-v&dOdnEA`&i7R+jv&SvIO%T;QJ0l z?||-?PC*&S`1zz9gr<*xv<9?xg!;KunaI7aRdKc6MaS9^>b|~p(5fUkKdN#g@vD)c z`|+LavWnHa1NaPuo+sU_IwK70ew4V;N=N5>ts_`?l{TXLyyr}5w8ZWN#ES8n7 zkc#J9#eoz}BeRL^_TrW3rG93Y zDz(596`yaGArV4;+Utg}0?-s(-$PdhO4ku2QzaK+_lE(a1B{MRh)uIwXl6D(c@>`B zMK76L@b*azUbek)yReozrG@O)J;eZEP}ow%TpYc3fh2LbQus>YXprq65{35kMz3+6{w* zb>6;Yrtxt~4D>-NTg3=3E{0ytVfu9b&bL;9Ky#z4mDq;KNPF@i#U4swH=@j6XdhjG z>?&dpSNnOYMYuFtGnijOQewQx1(KXZd^8^Qy-W5G%*kBY~2p5O&X)93z-&aFTP!Sk!94^P%^BAbYuCe z_~3Ce;YHvK{icAS;z&vLXbM!`x+Um~^PCAoVeK+kUOTZq4_7~5B0y=2XC^pPDzV^g z4=8S?w7^8E;dEv6f-w74Eml__LjT$G1fMq0*thIuC0vP&M4Tx)-$YldYKPU32Ul|@Vj8U*VMuAjinK3&@K>J}o+`bhwUw%mp*D82X`zN%unH zDL!wiauR|$4PPza!;9qgpl*ds8Hi_uojw@XKVkzz}mvs*})C>IJPhC{;Q0;yaSD%t&ebpQ8&-c36D=nOwak_nb^q8}TY zL@;Zh+u4U!W|S@h)hC!BXb%p6erdRRroC|cS>YB2fSX#898{r?sAM+_0G+~{{up>j zXE#^fhk)FNTIuO7AefWtfXW{M!H+=pEL8C}%Mzfvhxs!9H=%9canWpI1S>e!gQ+f= zE&-74&beB`Zuone{=Q6C`Fsd6nZ;fVtP2lhjlYp$MF~w#Zh<&sNS9b@+)CQmT@-td z?p@sAdFG+#7+eA2n2)qe`oo2|>^miJh*SmHxeXMc&hM^i& zt|%&0*{8~c7FVDeKTr|&xbr80HPn3>)O{JH%x93;noIJ#tC7>JR3oPeywH!2AxX54 zFw{z>qa_gl13d4h>||!lu!5g+9PKdpZ_!Pd3bT9ON=ViJ#jK@5T0jOMz>T+~i9kKW<&D=~@)O9KEdm!E6 zj9?Py7KG6C>60kk1zm@PpsT4}p?>CbdLVV5!k%-N})$N^pX{jb9MPCI~V zN%%pCMBH)#La5HKM{@WZ9dmGWurGeli+0jX;33v$ZG&e^6zq`ER4{(L=0>OsRKj-> z1V2}!!MAJ`05s{+!x?>JAO)Z+P{Yq-HGM87DiPAaHGj7)7%n6r#SgmuV#<&w8Iq2p z8lOQxKEsPMAY=Y-0=N@K<5wGQ%=b~N4?~FZTUTMlKql$x>fP-U>4d9(ZmU%>s^4Lt zV~XAknXBRp5k+Pp4XM4?ErteackzR^R~Xj-w?y=Ip4|>>ClIWoMcgr>w+i56+HN32 zS(}A}`#`@B(lTH}!9*Oh`yEv?OPnq6Ak!l`_e0P786z&0o*zQc4*0WM&9PF|XjUkC+X*N5X0fm3Y#wu*FVGv-rZ` zQ{bBdc;=Ffkac6+ATED<MjtjaI@ZD3Wk;Gk){sr2G$+qCKMlU_3Ym9LlKrU<#zKyOX z^djlr3BJ_LGK{KW2Vc^Gqcf+FH7RL0D3A&GXs5IP1PfTc((q^oOX4+1l8qf`=o` zB$Md$!P&Sw04d+x?JpaTgThMD53+CA0lY$wku=K2mKHYbk3p6@^y40_A>KFNzG?`#*BF3nZBjVU2EQn%W*OX6%Q8$`>k ztk=GVx9T9qC@VzUJS)yn%50m`-Z~dxemVW=4NAD)# z3{DTJ00X`3+uw~(iZ)tlG0H&hCm2=jsgXi5?`WRN{w@IG0Q*eC2R>9U2}X%6fOOgB z$ZK?iP@nUd*FXj^Bv{EC99_{o<;4<*gsRxKcgA!X3hC} z@6ojt4XX4E7(D}!rVWo3B`I~h)hAbkmuGrs#Bq#JdwG%;1nJH8EZK&f#@a%p{_hUg^%{Wbe z*{S0g7~fMEBd0gc40~F9ZL3|zXL!LtelW9pmRk&knmHF1 z0v^>;V}-SUhtd8HT}=ojZY=uD==bz|jLRA>K|i>{ZKAKWR*Jw?TRR2IU~c!+OGX$k zz6+$1v)R=SD#K_+TJegOfQS+$s-34``A{yT%@Z( znyR1&@Lv~P@Ni`ydtG3_J{|lz9W2dcK_)-qQp`*kd=%msQ|B4CgEOl!hbMG_40(Ef zLO#eJAg9@@F>_u!Hk)N)zR$L(kZVQ10=UkoT-MpI4&cYW9uaS9IDJBE0lt@Mw#N%q zV?t%~VQPhtn!IqK&sDtSsn520JAn3&!Q&iQjv?{FM!qri(gKr_y>y6o2iflh3_mu^ zp&k(X-pzJNy2I!JpO>^IEz0yw8S+U`NMs2^OOLM5)fI&6D#CSj{_PZeP1P9>P2@gR zN;=a4Lq>lABXKA7O3dQ6=bxnoZ6g9N*dJX6`pY2r!cG8T$U4&C@pd*aepy%J1 zSGta8^5YRoj}+3BT!9e}JyY^D#Mvrr6cIW2bMrL#I$eDoo>Pd_T}O7;7~J^z&6t~W zDDgQY;u~~O9Q@a?)^Jd5^s~t9&+gibeK_b&?SK8MR=Tjs?cMF-%-<0E0-~3%3lStk zY_%d0<19q{EMdTBLBTH$zX8(q@!bL79{^;+0F-W@Dm8ar6qT|x0OZ88hY&x|{zgAA zk7eRIHF=}G9fQmmWRjv`pkD=oUtKa2E+KSdktf6sX2LY%*M5gNMhuy`pdFmybI*i> z7D8wOyPVuZa)$07qL(E1;Gi4FD&z1es(gwUODA13Ie7gHH;=}=)*6ibPWYc9AWl*K z5>zXf`fVV150-0?uXFCV(Vnd^?zh3bhm!7(Fe~Ko3MRijGjsPf?XCN-E@1WixXsk_ zVETnv{P1?WY{H6x_2JfJ!yZgtVITus-6ubSoq#0~9`@xGT#O>)+CYDdZ8FlY2{rHK^9kIH0au2&YHZ!0shmct18h>yxtY>YM{7JKmR z-OvU4FYZI$o24+<8N=ljfAGWaKE+7;mw;e?LRqw*!bT-$iXORsw2D{^vLw)@Oi%}% zbs|v`DNZk(~)~{`9|j1lR&e3U{6vnbXvKzZ>iH%`4%sV=RXH)%ZvXU9YpDP zVVvF5tQQZJ36Y;7?K-rjTMDR>lpgXIHC4~;ic&|ZgZs4+qo-4+BTrDpwXxp^%VaRR zR+@+hF3T@&rWl?H0owj7+;X4Rz|f+mJxn&vnYKdKkG=~X^t6BrjG;}HCRqU z2$vlcAemfz>Crq73Q!1RC>*jqj}A`3S8Xh6n7H(Zufl1NjsS1B(mOSMTJ(}}AkB0W z**!Wx4sL@iHkFM#Cv>G-OpXVttpO>2BNtCgYkRo`Q~pza$q$yH0b^cne>XNC@?!8+ z_lb5}pGKXB$)~8Y1yoxg=o0$@_Q0cuez>G=LBSSo(SUNUPto5~v^@mX#RV-CVjosa zV9IH8k#}>8SeSWF5(Wd1wB~7EbX+#uWlX)nJ|0+c^tMox?xSECIdGn$pOJ&AAIOYn zVboZLLJc>qj2Zkt-?yO8EmwsHrAX68D=lsShC;j0*@!}7FRWl?22L*vF5et&=~l*| z-eYj}v1+lP5cuf?;Ly~0nVvm4H;9-W;^>Ew%<6V4xy8u9P_k!|$%|XLd>H0wWK5d4 znwCxJSa0+4vZwF3T>uC$T=OylbqtM>k);bL-Wp8Uy;X8rH(`LM6s%z$+|}ulr2C`? z8#_WO*=yc4p^4;;FU@6&S=3t&ad3=UasR3;d=;{Cg{u6%$ z{3bwK_7SiXUSr1z`gW2b-vm^yiM+^IS^px zGK1W1r9nonI$*Sj9vYS3my{LApW7;AMVU?d93Bf!S7o?&fNw`@p#@P*H6vv_N(4ag zv-2JNuUHkQj9Qfmusrv7$DXc}`4-kOi-gkPY`ax9Uf9-CFa++bS$`gk{6?aq)bVEU z0|{y#0vFnIYd$tnsanC?R}(lW&-}=GW`@R6chTuxa3S+)NFv3}6P~7-6H4uiQxx|d z4h~sSN*H5GS6YF`A)?1|9HJT~gw~;R3DQ5L@;?NbY_y@pk(;WMG(m>W(M!eN#rOUCp8eK)#J}-^RE{hY&L&R_L5WBOms#R5G&;IGr6^bfvQ~Lt0Y`Pj2bQ?X5v&PY15EtJrZ_)r%s! z;H`M3Uof1GU9j+0ZU2UkMr!uA;jVb0ko(w;;0;mcx?<+&a&3Adb+I8fbKIj zQpV}!9>%z5rV!7(s@=N!dJ*|^mGL5S@8Nz~^nvz2t6^kL;x8|n6zk7YZ)UiSSufGO zmOp}2NJB7Ci@6DjwIvuW=RHBe@OQgCb8eq{03NZJSvXTUlH@&FIGERiZ;5hf02lng zUf~Kp7f8E6X7tGaQT*!Ijw6@K4>uTfe)C@XxIh23-z7mYMk0Dd#4fTD=&8#F_%mU>Bq zbsfn`x^gzJHRFHujl_5n8QoN46H`qIh#2+*(aO{cmrzOLVpLvP#7T zD3r|D%>GsP-=wQ=qF_1b5@NrH$<&3eUF3p$r`NUZT6B!UIW$FgS6$sP%Vl`_XkVNG zmVd%P{{;9ZvxURI#k0S~+doU`&oa0om5s+#E+3i`7$9uE146EDp>0;?VU!zf%s|Ha z&_UcCN;lYCN?yPfzy~EFZ*^HN3AvhPe7e5fUQ8LteU^V4ApiC}R2hg4@yQIiDXws9!nABt zaepxapo64$AZ+~Vg04achm7$(QfKHY_TGSA2ApWmk{c;g$tsz7e4bO1l>;0;Jwh-bjg_#LYObZ=FxJdw6Ig+riqOV zeGQ6FHrvJZlp|+K{urCN&BAi&H}gQB1wkMkECYh{Uz`cUb1(k=MWCvP=Y=o+Pt>EQ)*Dm8?3UYc*GmKVL>S~{Q6LhAuEarUDPi1S*8*R_(dquxa#b~s3z*;RgomC81bU0BLP=F4O#7!i;@M@(g>RaD5lT zzmV~N?#12R?bhO#7|5-9@wFgR?pRm;?-}xE%UlEQzehGav@{E)QddL!+V;Y)Tpk3Y zGuS52JqPhjU_yvn+wGVIyfKud&ynA!&>+vIl&18Om~~#-ODhbC6>7tekJ8r^!iM%h zdXd|-KxJiUamr%#Y8u6qdk{uBMd_S|M+A6LD`G(bFg!nE@oQs1yN1s*&*0p&e&jD)m>>L*7Kc15flGMF z7+btGUWB25T^mY#jJC;QV`QSgNaEmQ0;(+CaDcF?W$s6R#og^zZ=ZeFXna;e5T8{l54Zp# zetzErpNCQ$LWG@?CP6dErvs-R9ub&M>0pDl+Ouz`=ZP8D(sXyuNxuK?Lt1t8DJ%aAff4<;*I zq644~aE-L2Rfh(|!y+o#Z3FE;jeGj^EOa@;vvWLio9;Ux$vf)VJi!ild!TL!25^|Y6*)J4)Gtjcl3Ptq$g`Fkvc`$T2g5x*>zg9d2{agxt0~8u; zk*S#jbeS-?{>`lE1P)%ur!0OMLFGsZ@adVHK~`!v=P6L{Je{5g{n$S&%^tV0#Q;>t zgUaN0Y_?~5gLeS9;estDPTUc~1Z@z}5YdFGtEw)a`Aqi|AQ|I;iz-wS=9f+8kljRX zAUCD^%zuGCB5GS{g}%9ANc(*3&UX7U-abj?#{g(T9O0gw)s0pXkH=)_R=dq-b1*Nf z!wA=pZMTc6(L<}A$xk*vAk@yl6ACfL;^qg;q0^uVQOomeexQT6S!D8YP)!(se(WRs z6C}(ezCV#!djsUm7o9>86bb1UWmNG9V>wv*V6+a5IsvC-^d&~gt3WU(cB21&jU{93 zQu+Pg+b&tAEG^q^&pt?hNfRKQdkDG$Qua#tl*xJ)<3Zc<(~D*VjPjlEkxmYlxfuHk zY9pk(Olg%;b{s+7+ZJZvyiI}_ESD|Cug}5?MSdM05ORXF>%`)yv%-i^xPGq*f41DD0IHu{_^uX z=TEfKV3Q7Dgfo<4fobM!yClK`6MHy#j?ro}g?k)omU{F$6QnDRw=3=G;!h)%=|i4t zLS9+>^;y2@=mqZQRxp(5X4kfV2x`0u;v_?Vl>R2)j@7H>+w!{gaZH@6E(^&~%*xh% zQ#%fwzX>6FRAa$dem8Hwd$V2KyMF*^aUJyM4i~3J$B@{bw;e4y#PK>3mZSvy2rNig zezu0n=M8r5Zlwuh7>AuFnAnkO7^oxTunn}^%_H*d8~~jzqiUygHJ%yJ$sO&s-)@(4 zv^8*bH`QGZc0uSOf@ckB;n_x1~OuPoMuOkeP zqhN`VuzD(!Fgsb%m`|lHR5SCG7^Gq(x*7ve96iL21E(|vf+@PHr8bVpTO|7s$#&=7 zqkwT4QQ0iph4E8{EON%}C>BCGlZ|sgn)C_KtFQ(W2lGI#cI~Y| z9su9??sj<*^Y|hJ@@HZplWJa+^Ez*)1OA>zKV*C%lSN0ABne$Hg57>%I7s$arfj|H)O*&^x>m|S^!zL$pZ=&)f!ukB_%fcE-Z@R> z&CPZ(ZDUZBNN~p51H?>gaaaOZVkSwpTa~ebX%%^i57l1z1*1D${5We>sbfg9M>mtV zI7{~!fL#FhYe0#!%D8UUUv_@`ag3@HN`L6VpYz#;oJqx&6$P2gGdxxy=UT@FAjin= zV4JgH06p7Pt*$=_C7-1GdbT~QtfBB57#B~sF^4-qZ+0+qSnS`AK*Sy*sxB27eTDA7 z0{T-*Q$+8c_BXnxbf3X%qJ0TYznjXJ;DCqFrZ@wFHfAVsIo#8wPFPb_JBZXx0DwcG99xqb(Jz- zZ7;Jt;5;hlz7Zn!K-GhrrH+!{W-Nx&h=9S4SwtZ%O8PaMnS@%;TQ>h4Z_o>4IF*r- z+E``CMDiFPFjk&Xdv`ONhz~}Hg~#G9E-!<}0X-j3`j=2c*Y+>z&uvTv?iTg75Rmij z@5awLmRun(i$Cm0r!Hx{m$uhm-7dRW8gt=N+>e0sa~r&MQmy)3<<@{yXX6%bRmeP& z34_UVYZS8aQZ6~}8<||+;DRvhBMcWY8Gb0XY&U($^T&by;{fE6L98CcPEMLJaxya3 z(G0=f3>m+!`2-5yZbzsqocT>Svr8|hz{A5n^V=(c z_M5DL_u4 zJelz{N;3FP4%{-CLdKo{CTc;v-Qtnh0<SXNK}P5`U&$sxcP0LYgt*C z``-3Ug>`v;2c|51$LyzmKWd}}XqVex@?me(q@8HD3y0;u@oamxFh?wEBo~DEbcj!f z@5%g)QmxALf;&&i6FfVi8bFc-pZxuZUVT*j{jY1cIwcL`4(z!?2UqB{Caq8L>?z(R zexcOytOH+Q%s>$p2(j#g0Qa8%m-yvWO$H<}oQVn{O=oy^D?)P~j4l!T?19!LG-aVF~|RdG=P`{yR$lPJl4dil`1quXbv?Ju`h`Lh^fP zF{Q=yOuOW)Y&cU8+yV-RR579Jm-4GL%dEFbGrzhAmTqpjh-7CKk8v$&SzWI2a~k84 z9-%R|wTg%AdGy*~iwy9Mjm;okR1K*5nGsx}bPic@tvp`WW(@v0_%Xe^UB(++2x2M{ z%uXV^2mM$$=?8*SDFiYn5l8;Ql=2_Fu)t{}Py?GWXeC()1m+2~YjJ^qHV9a%XNF9F zRRXOwF9^Tr-!)6N-bGvRd%lyu3K{Iigxe0lJFJD4FjF2Vc}Ry#H^kEHE-fJl4mKgs z)m@wIGKS7CGJ{c8>Jt53+HMy}H997RFi^|F36Q!N79)e5ti$uP7LL*DDnX_Lf|`{NcHV-AQ#^N#Co$NyXG@a^ zG6hag;q(*)^TP)aCjk|dbk#^lFMk5`;~{AAr*qb`0tX()XA!9DgI71&vpOj= zX*5i!DD<>YXGB2{Hs#qAIvaSs8PZ+9vt25^XjV7ob>7k>p$jaRG2u%Sx^#aTl{CnD zo~$lRt`&*0K2duHDirbzUG@0GKf~K67|jpf>Q|W40<;D{4i6shrA5|GaAUg+e)K76 z7-=)9yU3qw^d0tKaPAh$v@|P(k@;AOG6GV9SsCj@zD`T!0;X@;{sxinKgCe|B>+F1 zXT@sQp5@@yjaD*AB<6=e<+M*9VC1XVy8XwT>I*u&X9|fmGCQb-QRKqeF@@XvsQkVP z<3VfM{{FNN;piB*gW^Yf&Smt2R^z+dCED-9Tc!KxM}!&TZWk?{6OfUK2) z%91=bTN!w4t5s2Jd(i3;pEB@?;I%ZosKuA)$9&~@10b!E23~)eXGq=U_P2bI9}d>a z{2Bla*7F#x#3DWOtk8CXdHLl^W&SFZOj(`hu+WNB_LCvpW@H6#e*>n zQ?l4Zr&BJfP-t(YisI;SH#S>oaFh%K#fC9ZM3fHPt(nmnV;wrb2hQ|B;M^cHa9&*J zGUaU;B~(0Hq0`XbiT2rcoBfV5ih54wSAfb`K1W+%q)qcB^XW7Qui@SWgRD2AYQply zd5W*1$?-=9XkhLaC`bP&izq3*>A zWiMTOmMo4CU^n{qH_YZizwBW_CUu$zGF0P+Y{;32GW?z3QaGYuss-mpGS14xE8Y1FNV)kx3aalM*Lb%NSq^cnq0b z0C0E57*rjtc1lcu!1&%dw$Nu%4aMECmX$czoe-k>Bs0_tg zs}eJEvn8aM?qY6Adm-xB>@U60LHPx(=mYfk0BG?tzsepL4Y=j37hgIlo-yschpzV#Vee1>V&KHV2#Ur#RzoU`rMqXFyqo?2cB zoslrtNe=1i5F6(9u8%QfoqV|+bZt-jj>c*bV3rNl+r)?w*igy@=rGAEb2lRJ9=?tYaw5UL&63+jO zxBraMDuJS6{;|)YxUT(yeFd`PuDuc_)L%hEXL*!-b>Iz$te; zk7|Wr*|iJcCFn)isDZHx{5?3^E(R=~Ro6p08E_RKuOb^%i&zvPKF!-t&uhR3Hrg|V z{{vY0=UM*+-$1320hvT;pzm3+(^eQvGF@Kzud!=M(bS^KW_vciyIm$|#S8kfX%hq& z6M2w0(I&c;ZEDE4YwA-2Ai!93y{KQjF0Acd+AjLHd;rU{92Ww`?JA~}F$RI=JWXSu zj^ct*_ykt$LCz^9L+3RD3^V1Xyf4Ak+u@`8O1E?li0xo=Gw?mi zg^~?uI)(u*c$Ay?7h(kZ|AMxjXXK7wj}JeINbOL?Z7I$SYZ3jgL-A;jfiRdqEOEM4 zN)nKsA4a%5>B5wr$Y{;{U~meF4I7OB%!_t(6}1O~-v)wB;M;`;8@xSD&!;iNe>Vbld6b<_6+6e8JGthYxdD+2U6+5oEL+mVtu2P0`E8#cN_!wo)?Bo-a? zV`Qjjlo0{uMFkE$)bQIXeUG-u?cdts6@3paV%I2DFCA?iGS3j!JE`(Jy!ah8xmtk7 zvW3mEqA~xf0bO<29B1lA^#i5jt&nD11UOcn7q>HHFIwClN~L9x6^s8qy zX9NRt12QobV0nld?10Asqa8zGvuL5YhF}`$!6~H(ja8(iFRo#}t})t}Ui{U|(@<#4 zvlY631(@7+kw{jWMQhdRisKtUj2o&)mkiu|eK~7JAe5g0xH(&%R90 zU!IxLALMOpB2b;Aiif6U7ER^z06BrHtK04JPOOT&d_5zR(J^t@f9;osc$?NU56F~>lodK6+3jW%L|VN`6voz zOsESqJl7n42&m#5O4d}hc_!;lR4}(cCY6Xw*rgfjHvDZ4+Lkx?14R@B{{ZUh2P1Ic zo8RuD7ydG_I}P;z0O;LRn8iOJ{YhJZjEh_oI{isRMP+}IDq03jbKkYwxN_>%%A>Ks z`gVKf&MDWEBV4GY?5P>m7nBl)bmG3Y(Mnq2FCZle_z)F;e|q4Ui6@oucxOiQC$FQ} zAo$l2>PyhBep=UK4h*-}=~Nz>_4D(M+Ypr}=55&G2ek~OlUt+vO#6U-afh=rAPBY( zarP;7l)8E5F=;aR-6deT%(#9Ze;IK!t6NU2C4W_Pn)oJYOz-*^Zfka+MR zIineGES{YYn%T@F15^X1%|@UmG|c)%8|?+MPq7xxt;|-$WW1_98)Ty4kvS+?c7bMY zl6@BvYrZbw?{>S@S-*Owm!|>aG(sJBj65q54^nvw5#4E=&uwXThVI{hru@&a+v627 z!{FJ3XR^$R$p+7|{Rinxc*L{%syiR(@=WO*Ow*vjSD{*F3CN#4558!biP;pf$h-pG zx{6|lRxsWi)RnH4lCpq)u9atb9vWQ15>BAx;42sK-GLAWR1->DGyZ1A0tB&t(8{g% zI}F*M;PxaJKwGw^9_Mp_K;N(nqNB5!h{3JX1k@ceeRSE5uQo?K)E|D>jFw z`E3}jnTZdrS!P)@TPqowA2+?soun4m;lJYvbf}eDLijz-^DE9z$YX`Sa|P z5qdbS;aP`+&Zk3N2&PZTr_+dl6NFt&2C&3f7F1dWZm0B|9hX2=KAwUo62k~Jjxu+^z)^wJiCfwuR@JA z-bU>yO?f+|6p}#ByQ&IwsT@x#qpP2VbO%VR8C9g3$9b5<-*9Xk}^c;_Y1!XOkCS;MQHNVZck|P+Tjz6;9E}kS7sEoa}lW5c_lznldFyhlAKEe{= zho%YhfHcBS)*xFuc^1)g#)3u9aRcz^-#D5cdzqu6%JcZBKY@FS7l#_ggxS;pxRIt= z&t~%93g+QCC}bHG9@Sj(Ye>o0Anb<$TneQtD5-Z2of)H}a!0*`%CK;21mG^WV8vyNeDZ$L-B9u>I6JIa{oJN_D1dVW-IA&b{NC&j|?Wv zs%6s@J>N-xKf}0{vhd(n6|$^&CQ$huw=;@X^)#IO$HY8bt@oU zqF1}yBHpyE`Aj&-v-@H4{h)7rT4w#HVV;`{%-H<&tJF5XVXfGbD7G->`&;BEw?r*n?-$>Nk zKbXxH|A5MY00}!3EilTD%9XK%6&fg`0z<3Cb#{h4*ld^a-R&~jYL_I52sN^VP3))< zG|}y{SWe;q$kSrf3Zt9(PzQV$O*8ne(A5fWCs1QTX_3FnoO!!wQjEgZ>0PEfCa2r8#V?;3SM#vGLED)UOU_-$IRQaI>!9h2`1K9O z_#PPbJwP>v5QU%#lBA=l;ylIt+Rk>nHD=BHmuAb%f2hV7`6U5L+1*Qva0|@+sdD|` zMP8I0{|?6e4m`wQ!|6Pb;li?KjCMc=2b4NW9i?hfaxmrXl((hvpMu0I3i^B|-;DoY zNpic@bNc@ZTI}14GZEv?j<-%h8Dp@xLC+ps{cF7VHIzgW``hkj&(my!7jWPFkh^xZ z<5_HHaC0}SXQ_v|9vt$h0Rbq7N zV>e6#C1W$|B}oLDIiwbS#iXYUsJ8H^gX~Jgn<>4SXU|Z2=6JiDMw1U@3NJzfUJO3K z;l;aX@h)PEzWrrQ=6lV2 z^Z4b)w%~T)OGoigNF6Hw#aLVp&`iCqX@jx1-|93bic% zyi*J`=~L{>zjjW=kqz^eV9_VtiYTB3Rbc=VkVOh;G|D+WhQxqRRku?)HUCv4<{BBN z#5|}JL>~MT&nuKCkyBNF?C?(dcOicO=$8N2P}cJ6NAZ43WdQGwHR@vaPQ2gSNZ*g= z1HcD?4*@B|ZNNu>ROc=r`vTelpO53Y1;~1s-hls~1lH33-%hvuf9yZ>|0&qyhW|qd z0LvoTAFv;wpFV+i`Y?T$Y5G5(EBXI@D2HNgaF0WNTfvQtHJ1SO>V%fwsaKBKE9sx{ zAjcDq85~2{|I?N@E<6d`4SWi?2lzB_5cmvmFYsC55b!zRKH&4f{lEjj7l1DU4+1sd zFz_Xy4)g;9z#uRL907)b5nvP;1C9ba@i=D7pG5j7@EGv8?VI}lnk(sFB$z_^Zh~W#Cj!&8IC{ z=}FK`L(=DQG47m|63>VWNY$)DU7mxhpnn+k(N_uM(03WpXc2U)c)E>y&A;i>13n)T zR-9+$cYB`1NBTBlMoNE9OhNiJXgvYW8H3kFMq1@cGGP3>3H4ZGjI4Bp+X;MU-$r2E zPm8O<=2EbQV`xV~%HEn87-&h|(JDq)A^+w2FH25~ph>Hr1^tr9LI#(`H10K|>p=Qu zCt2v=pZzlR>DK-xP<9KI2C&;7`6vAZ=7B|6CVPi&u&;0am)n0Fawvi-=b@@BI`PhS zq5tXG|7a^S4W{h;kAt!SOPIrKuq>a<1rLPhZe_@9sRWQ^gKKIvNY6Y#zaJdNC4 zLfu(#Rz-wi9~XzwLzTgKR`vw*kW-!22=L#t{k@L=EdBRFpW{>=JvK+>5?Xx$^^C28 zzS{pJq_tONZL~5{ID@NG{-;5;gv)@` zk4dgucGn<}Sg&{a)AlX>ccXt!a$c8XRJzGK|Hwiz7o-(J{yxUGS3rYf~bi>#8NsgKm3UIZqOKpx)pKt z?etr}elxmo-~(5Ml|p~L7vKBapOGI|`@d3^Ctv9Y{pg_I|7nN)(5D~q|5hE9F(h3> zzwLKT$f01X-3|p@WYeGR&`+MdE;niF@k`(YX4}cec*;2)V}(H_s!k%7$zyckcv}PJ z(37&iVDvwa(gmav$eTwW%cwVr*}V=7;Ei+BA})@;jD@Zu?;LWI=%LN%poaH2{x@^| zSP>Y;%7A(IV=W+vD~W$OdUDpw(LRY*f9=thXAra0B?k*0<)8??Qx{cF>!Ai)r0sdf z2k3#coEn}PPs!fi@+*h|L!RbfGw;tsqkMSW2d!i@* zoYaO)hgUbxg--dvb za^4fd?4<_3FxRQfeT5{>Hcd-U=~N~E?+3VgW3J_>GugAqWz8qJ45MCJGu8b~{fF<4 zkPt&#ZykB@{qIg(zxnx%?Vru!U{FFtb2}Cm7xqkj`cd|a5BL1dO;`uu-eDYlin+4O znIEGe&i^WnS>#2OIE2%PTs3>StrqTMa1CH6oH8OKkw{d_80m->G6ESbX6QLB5{Vh< zs2)uRQrH(pX}Mj+dh%mj1|QcFGow1LcY>%PrU(6+;ZJ8Yzdx&M(Htt&^^~58`Te>+ z61mnYuBWnkM305FbUGE&LNQ&}QsEK5rjNv;*+4iK&IUqC@nFb^gpDX_H$o}1_LLDY z8nx@WTrLw-if1Fyus@KAXt7v0poP-WpcWg+#Kz{dXX`riMIW4Gt5;F{a=b0F%P9o*UxR$<~)cc(pv`GJ1`X0v&X literal 0 HcmV?d00001 diff --git a/2 lab/.vs/ProjectSettings.json b/2 lab/.vs/ProjectSettings.json new file mode 100644 index 0000000..f8b4888 --- /dev/null +++ b/2 lab/.vs/ProjectSettings.json @@ -0,0 +1,3 @@ +{ + "CurrentProjectSetting": null +} \ No newline at end of file diff --git a/2 lab/.vs/VSWorkspaceState.json b/2 lab/.vs/VSWorkspaceState.json new file mode 100644 index 0000000..5ddea5d --- /dev/null +++ b/2 lab/.vs/VSWorkspaceState.json @@ -0,0 +1,7 @@ +{ + "ExpandedNodes": [ + "" + ], + "SelectedNode": "\\.gitignore", + "PreviewInSolutionExplorer": false +} \ No newline at end of file diff --git a/2 lab/.vs/slnx.sqlite b/2 lab/.vs/slnx.sqlite new file mode 100644 index 0000000000000000000000000000000000000000..1a947146d52e9c8ee668bb9e199e54dfb5b50de6 GIT binary patch literal 90112 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 RfDj-A2mwNX5V+$6{s)@msLTKW literal 0 HcmV?d00001 diff --git a/2 lab/build.gradle b/2 lab/build.gradle new file mode 100644 index 0000000..b7867b9 --- /dev/null +++ b/2 lab/build.gradle @@ -0,0 +1,28 @@ +plugins { + id 'java' + id 'org.springframework.boot' version '3.2.3' + id 'io.spring.dependency-management' version '1.1.4' +} + +group = 'com.example' +version = '0.0.1-SNAPSHOT' + +java { + sourceCompatibility = '17' +} + +repositories { + mavenCentral() +} + +dependencies { + implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.3.0' + implementation 'org.modelmapper:modelmapper:3.2.0' + + testImplementation 'org.springframework.boot:spring-boot-starter-test' +} + +tasks.named('test') { + useJUnitPlatform() +} diff --git a/2 lab/gradle/wrapper/gradle-wrapper.jar b/2 lab/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..d64cd4917707c1f8861d8cb53dd15194d4248596 GIT binary patch literal 43462 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 literal 0 HcmV?d00001 diff --git a/2 lab/gradle/wrapper/gradle-wrapper.properties b/2 lab/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..1af9e09 --- /dev/null +++ b/2 lab/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/2 lab/gradlew b/2 lab/gradlew new file mode 100644 index 0000000..1aa94a4 --- /dev/null +++ b/2 lab/gradlew @@ -0,0 +1,249 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/2 lab/gradlew.bat b/2 lab/gradlew.bat new file mode 100644 index 0000000..93e3f59 --- /dev/null +++ b/2 lab/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/2 lab/settings.gradle b/2 lab/settings.gradle new file mode 100644 index 0000000..0a383dd --- /dev/null +++ b/2 lab/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'demo' diff --git a/2 lab/src/main/java/com/example/demo/DemoApplication.java b/2 lab/src/main/java/com/example/demo/DemoApplication.java new file mode 100644 index 0000000..732f1f3 --- /dev/null +++ b/2 lab/src/main/java/com/example/demo/DemoApplication.java @@ -0,0 +1,85 @@ +package com.example.demo; + +import java.sql.Time; +import java.util.ArrayList; +import java.util.Objects; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.CommandLineRunner; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +import java.util.List; + +import com.example.demo.comments.model.CommentEntity; +import com.example.demo.comments.service.CommentService; +import com.example.demo.messages.model.MessageEntity; +import com.example.demo.messages.service.MessageService; +import com.example.demo.posts.model.PostEntity; +import com.example.demo.posts.service.PostService; +import com.example.demo.users.model.UserEntity; +import com.example.demo.users.service.UserService; + +@SpringBootApplication +public class DemoApplication implements CommandLineRunner { + private final Logger log = LoggerFactory.getLogger(DemoApplication.class); + + private final UserService userService; + private final PostService postService; + private final CommentService commentService; + private final MessageService messageService; + + public DemoApplication(UserService userService, PostService postService, CommentService commentService, + MessageService messageService) { + this.userService = userService; + this.postService = postService; + this.commentService = commentService; + this.messageService = messageService; + } + + public static void main(String[] args) { + SpringApplication.run(DemoApplication.class, args); + } + + @Override + public void run(String... args) throws Exception { + if (args.length > 0 && Objects.equals("--populate", args[0])) { + + log.info("Create default items values"); + final List blank = new ArrayList(); + final var user1 = userService.create(new UserEntity(null, "sardq", "aasd@asd", "asdsd", blank)); + final var user2 = userService.create(new UserEntity(null, "sardasdq", "aasdsad@asd", "123", blank)); + final List friends = new ArrayList<>(); + friends.add(user1); + friends.add(user2); + final var user3 = userService.create(new UserEntity(null, "sxzcvardq", "aaxsd@asd", "asfgd", friends)); + final var user4 = userService.create(new UserEntity(null, "sarbvljdq", "aacbxcsd@asd", "zxvb", blank)); + log.info("Create default posts values"); + final var post1 = postService.create( + new PostEntity(null, user1, new Time(System.currentTimeMillis()), "test", Long.valueOf(0), + Long.valueOf(0))); + final var post2 = postService.create( + new PostEntity(null, user3, new Time(System.currentTimeMillis()), "test", Long.valueOf(11), + Long.valueOf(2))); + final var post3 = postService.create( + new PostEntity(null, user4, new Time(System.currentTimeMillis()), "test", Long.valueOf(40), + Long.valueOf(15))); + + log.info("Create default comments values"); + commentService + .create(new CommentEntity(null, user1, post2, new Time(System.currentTimeMillis()), "my comment")); + commentService + .create(new CommentEntity(null, user3, post1, new Time(System.currentTimeMillis()), "my comment2")); + commentService + .create(new CommentEntity(null, user4, post3, new Time(System.currentTimeMillis()), "my comment3")); + + log.info("Create default messages values"); + messageService.create(new MessageEntity(null, user1, user2, new Time(System.currentTimeMillis()), "hello")); + messageService + .create(new MessageEntity(null, user1, user2, new Time(System.currentTimeMillis()), "how are you")); + messageService + .create(new MessageEntity(null, user2, user1, new Time(System.currentTimeMillis()), "I'm ok")); + } + } +} diff --git a/2 lab/src/main/java/com/example/demo/comments/api/CommentController.java b/2 lab/src/main/java/com/example/demo/comments/api/CommentController.java new file mode 100644 index 0000000..75d2c07 --- /dev/null +++ b/2 lab/src/main/java/com/example/demo/comments/api/CommentController.java @@ -0,0 +1,78 @@ +package com.example.demo.comments.api; + +import java.util.List; + +import org.modelmapper.ModelMapper; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.example.demo.comments.model.CommentEntity; +import com.example.demo.comments.service.CommentService; +import com.example.demo.core.convert; +import com.example.demo.core.configuration.Constants; +import com.example.demo.posts.service.PostService; +import com.example.demo.users.service.UserService; + +import jakarta.validation.Valid; + +@RestController +@RequestMapping(Constants.API_URL + "/comment") +public class CommentController { + private final UserService userService; + private final PostService postService; + private final CommentService commentService; + private final ModelMapper modelMapper; + + public CommentController(CommentService commentService, UserService userService, PostService postService, + ModelMapper modelMapper) { + this.userService = userService; + this.commentService = commentService; + this.postService = postService; + this.modelMapper = modelMapper; + } + + private CommentDto toDto(CommentEntity entity) { + var model = modelMapper.map(entity, CommentDto.class); + convert time = new convert(); + model.setTime(time.dateToString(entity.getTime())); + return model; + } + + private CommentEntity toEntity(CommentDto dto) { + final CommentEntity entity = modelMapper.map(dto, CommentEntity.class); + entity.setUser(userService.get(dto.getUserId())); + entity.setPost(postService.get(dto.getPostId())); + return entity; + } + + @GetMapping + public List getAll() { + return commentService.getAll().stream().map(this::toDto).toList(); + } + + @GetMapping("/{id}") + public CommentDto get(@PathVariable(name = "id") Long id) { + return toDto(commentService.get(id)); + } + + @PostMapping + public CommentDto create(@RequestBody @Valid CommentDto dto) { + return toDto(commentService.create(toEntity(dto))); + } + + @PutMapping("/{id}") + public CommentDto update(@PathVariable(name = "id") Long id, @RequestBody CommentDto dto) { + return toDto(commentService.update(id, toEntity(dto))); + } + + @DeleteMapping("/{id}") + public CommentDto delete(@PathVariable(name = "id") Long id) { + return toDto(commentService.delete(id)); + } +} diff --git a/2 lab/src/main/java/com/example/demo/comments/api/CommentDto.java b/2 lab/src/main/java/com/example/demo/comments/api/CommentDto.java new file mode 100644 index 0000000..51a35f7 --- /dev/null +++ b/2 lab/src/main/java/com/example/demo/comments/api/CommentDto.java @@ -0,0 +1,62 @@ +package com.example.demo.comments.api; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; + +public class CommentDto { + private Long id; + @NotNull + @Min(1) + private Long userId; + @NotBlank + private String text; + @NotNull + @Min(1) + private Long postId; + @NotBlank + private String time; + + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getUserId() { + return userId; + } + + public void setUserId(Long userId) { + this.userId = userId; + } + + public Long getPostId() { + return postId; + } + + public void setPostId(Long postId) { + this.postId = postId; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + + public String getTime() { + return time; + } + + public void setTime(String time) { + this.time = time; + } +} diff --git a/2 lab/src/main/java/com/example/demo/comments/model/CommentEntity.java b/2 lab/src/main/java/com/example/demo/comments/model/CommentEntity.java new file mode 100644 index 0000000..b1724cc --- /dev/null +++ b/2 lab/src/main/java/com/example/demo/comments/model/CommentEntity.java @@ -0,0 +1,78 @@ +package com.example.demo.comments.model; + +import java.util.Date; +import java.util.Objects; + +import com.example.demo.core.model.BaseEntity; +import com.example.demo.posts.model.PostEntity; +import com.example.demo.users.model.UserEntity; + +public class CommentEntity extends BaseEntity { + private UserEntity user; + private PostEntity post; + private Date time; + private String text; + + public CommentEntity() { + super(); + } + + public CommentEntity(Long id, UserEntity user, PostEntity post, Date time, String text) { + super(id); + this.user = user; + this.post = post; + this.time = time; + this.text = text; + } + + public UserEntity getUser() { + return user; + } + + public void setUser(UserEntity user) { + this.user = user; + } + + public PostEntity getPost() { + return post; + } + + public void setPost(PostEntity post) { + this.post = post; + } + + public Date getTime() { + return time; + } + + public void setTime(Date time) { + this.time = time; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + + @Override + public int hashCode() { + return Objects.hash(id, user, post, text, time); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null || getClass() != obj.getClass()) + return false; + final CommentEntity other = (CommentEntity) obj; + return Objects.equals(other.getId(), id) + && Objects.equals(other.getUser(), user) + && Objects.equals(other.getPost(), post) + && Objects.equals(other.getText(), text) + && Objects.equals(other.getTime(), time); + } +} diff --git a/2 lab/src/main/java/com/example/demo/comments/repository/CommentRepository.java b/2 lab/src/main/java/com/example/demo/comments/repository/CommentRepository.java new file mode 100644 index 0000000..b60c20f --- /dev/null +++ b/2 lab/src/main/java/com/example/demo/comments/repository/CommentRepository.java @@ -0,0 +1,10 @@ +package com.example.demo.comments.repository; + +import org.springframework.stereotype.Repository; + +import com.example.demo.comments.model.CommentEntity; +import com.example.demo.core.repository.MapRepository; + +@Repository +public class CommentRepository extends MapRepository { +} diff --git a/2 lab/src/main/java/com/example/demo/comments/service/CommentService.java b/2 lab/src/main/java/com/example/demo/comments/service/CommentService.java new file mode 100644 index 0000000..5c2c51a --- /dev/null +++ b/2 lab/src/main/java/com/example/demo/comments/service/CommentService.java @@ -0,0 +1,43 @@ +package com.example.demo.comments.service; + +import java.util.List; +import java.util.Optional; + +import org.springframework.stereotype.Service; + +import com.example.demo.comments.model.CommentEntity; +import com.example.demo.comments.repository.CommentRepository; +import com.example.demo.core.error.NotFoundException; + +@Service +public class CommentService { + private final CommentRepository repository; + + public CommentService(CommentRepository repository) { + this.repository = repository; + } + + public List getAll() { + return repository.getAll(); + } + + public CommentEntity get(Long id) { + return Optional.ofNullable(repository.get(id)) + .orElseThrow(() -> new NotFoundException(id)); + } + + public CommentEntity create(CommentEntity entity) { + return repository.create(entity); + } + + public CommentEntity update(Long id, CommentEntity entity) { + final CommentEntity existsEntity = get(id); + existsEntity.setText(entity.getText()); + return repository.update(existsEntity); + } + + public CommentEntity delete(Long id) { + final CommentEntity existsEntity = get(id); + return repository.delete(existsEntity); + } +} diff --git a/2 lab/src/main/java/com/example/demo/core/configuration/Constants.java b/2 lab/src/main/java/com/example/demo/core/configuration/Constants.java new file mode 100644 index 0000000..d9c6b7c --- /dev/null +++ b/2 lab/src/main/java/com/example/demo/core/configuration/Constants.java @@ -0,0 +1,8 @@ +package com.example.demo.core.configuration; + +public class Constants { + public static final String API_URL = "/api/1.0"; + + private Constants() { + } +} diff --git a/2 lab/src/main/java/com/example/demo/core/configuration/MapperConfiguration.java b/2 lab/src/main/java/com/example/demo/core/configuration/MapperConfiguration.java new file mode 100644 index 0000000..a5ad6f3 --- /dev/null +++ b/2 lab/src/main/java/com/example/demo/core/configuration/MapperConfiguration.java @@ -0,0 +1,13 @@ +package com.example.demo.core.configuration; + +import org.modelmapper.ModelMapper; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class MapperConfiguration { + @Bean + ModelMapper modelMapper() { + return new ModelMapper(); + } +} diff --git a/2 lab/src/main/java/com/example/demo/core/configuration/WebConfiguration.java b/2 lab/src/main/java/com/example/demo/core/configuration/WebConfiguration.java new file mode 100644 index 0000000..762e85a --- /dev/null +++ b/2 lab/src/main/java/com/example/demo/core/configuration/WebConfiguration.java @@ -0,0 +1,15 @@ +package com.example.demo.core.configuration; + +import org.springframework.context.annotation.Configuration; +import org.springframework.lang.NonNull; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +public class WebConfiguration implements WebMvcConfigurer { + @Override + public void addCorsMappings(@NonNull CorsRegistry registry) { + registry.addMapping("/**") + .allowedMethods("GET", "POST", "PUT", "DELETE"); + } +} diff --git a/2 lab/src/main/java/com/example/demo/core/convert.java b/2 lab/src/main/java/com/example/demo/core/convert.java new file mode 100644 index 0000000..5973126 --- /dev/null +++ b/2 lab/src/main/java/com/example/demo/core/convert.java @@ -0,0 +1,17 @@ +package com.example.demo.core; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; + +public class convert { + public Date stringToDate(String string) throws Exception { + DateFormat format = new SimpleDateFormat("HH:mm:ss"); + return (Date) format.parse(string); + } + + public String dateToString(Date date) { + DateFormat dateFormat = new SimpleDateFormat("dd.MM.YYYY hh:mm:ss"); + return dateFormat.format(date); + } +} diff --git a/2 lab/src/main/java/com/example/demo/core/error/NotFoundException.java b/2 lab/src/main/java/com/example/demo/core/error/NotFoundException.java new file mode 100644 index 0000000..586af3c --- /dev/null +++ b/2 lab/src/main/java/com/example/demo/core/error/NotFoundException.java @@ -0,0 +1,7 @@ +package com.example.demo.core.error; + +public class NotFoundException extends RuntimeException { + public NotFoundException(Long id) { + super(String.format("Entity with id [%s] is not found or not exists", id)); + } +} diff --git a/2 lab/src/main/java/com/example/demo/core/model/BaseEntity.java b/2 lab/src/main/java/com/example/demo/core/model/BaseEntity.java new file mode 100644 index 0000000..674ddfb --- /dev/null +++ b/2 lab/src/main/java/com/example/demo/core/model/BaseEntity.java @@ -0,0 +1,20 @@ +package com.example.demo.core.model; + +public abstract class BaseEntity { + protected Long id; + + protected BaseEntity() { + } + + protected BaseEntity(Long id) { + this.id = id; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } +} diff --git a/2 lab/src/main/java/com/example/demo/core/repository/CommonRepository.java b/2 lab/src/main/java/com/example/demo/core/repository/CommonRepository.java new file mode 100644 index 0000000..85e1e6d --- /dev/null +++ b/2 lab/src/main/java/com/example/demo/core/repository/CommonRepository.java @@ -0,0 +1,17 @@ +package com.example.demo.core.repository; + +import java.util.List; + +public interface CommonRepository { + List getAll(); + + E get(T id); + + E create(E entity); + + E update(E entity); + + E delete(E entity); + + void deleteAll(); +} diff --git a/2 lab/src/main/java/com/example/demo/core/repository/MapRepository.java b/2 lab/src/main/java/com/example/demo/core/repository/MapRepository.java new file mode 100644 index 0000000..6809ac2 --- /dev/null +++ b/2 lab/src/main/java/com/example/demo/core/repository/MapRepository.java @@ -0,0 +1,57 @@ +package com.example.demo.core.repository; + +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +import com.example.demo.core.model.BaseEntity; + +public abstract class MapRepository implements CommonRepository { + private final Map entities = new TreeMap<>(); + private Long lastId = 0L; + + protected MapRepository() { + } + + @Override + public List getAll() { + return entities.values().stream().toList(); + } + + @Override + public E get(Long id) { + return entities.get(id); + } + + @Override + public E create(E entity) { + lastId++; + entity.setId(lastId); + entities.put(lastId, entity); + return entity; + } + + @Override + public E update(E entity) { + if (get(entity.getId()) == null) { + return null; + } + entities.put(entity.getId(), entity); + return entity; + } + + @Override + public E delete(E entity) { + if (get(entity.getId()) == null) { + return null; + } + entities.remove(entity.getId()); + return entity; + } + + @Override + public void deleteAll() { + lastId = 0L; + entities.clear(); + } +} diff --git a/2 lab/src/main/java/com/example/demo/messages/api/MessageController.java b/2 lab/src/main/java/com/example/demo/messages/api/MessageController.java new file mode 100644 index 0000000..169dadc --- /dev/null +++ b/2 lab/src/main/java/com/example/demo/messages/api/MessageController.java @@ -0,0 +1,75 @@ +package com.example.demo.messages.api; + +import java.util.List; + +import org.modelmapper.ModelMapper; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.example.demo.core.convert; +import com.example.demo.core.configuration.Constants; +import com.example.demo.messages.model.MessageEntity; +import com.example.demo.messages.service.MessageService; +import com.example.demo.users.service.UserService; + +import jakarta.validation.Valid; + +@RestController +@RequestMapping(Constants.API_URL + "/message") +public class MessageController { + private final UserService userService; + private final MessageService messageService; + private final ModelMapper modelMapper; + + public MessageController(MessageService messageService, UserService userService, ModelMapper modelMapper) { + this.messageService = messageService; + this.userService = userService; + this.modelMapper = modelMapper; + } + + private MessageDto toDto(MessageEntity entity) { + var model = modelMapper.map(entity, MessageDto.class); + convert time = new convert(); + model.setTime(time.dateToString(entity.getTime())); + return model; + } + + private MessageEntity toEntity(MessageDto dto) { + final MessageEntity entity = modelMapper.map(dto, MessageEntity.class); + entity.setFriend(userService.get(dto.getFriendId())); + entity.setPerson(userService.get(dto.getPersonId())); + return entity; + + } + + @GetMapping + public List getAll() { + return messageService.getAll().stream().map(this::toDto).toList(); + } + + @GetMapping("/{id}") + public MessageDto get(@PathVariable(name = "id") Long id) { + return toDto(messageService.get(id)); + } + + @PostMapping + public MessageDto create(@RequestBody @Valid MessageDto dto) { + return toDto(messageService.create(toEntity(dto))); + } + + @PutMapping("/{id}") + public MessageDto update(@PathVariable(name = "id") Long id, @RequestBody MessageDto dto) { + return toDto(messageService.update(id, toEntity(dto))); + } + + @DeleteMapping("/{id}") + public MessageDto delete(@PathVariable(name = "id") Long id) { + return toDto(messageService.delete(id)); + } +} diff --git a/2 lab/src/main/java/com/example/demo/messages/api/MessageDto.java b/2 lab/src/main/java/com/example/demo/messages/api/MessageDto.java new file mode 100644 index 0000000..d92bce1 --- /dev/null +++ b/2 lab/src/main/java/com/example/demo/messages/api/MessageDto.java @@ -0,0 +1,62 @@ +package com.example.demo.messages.api; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; + +public class MessageDto { + private Long id; + @NotNull + @Min(1) + private Long friendId; + @NotNull + @Min(1) + private Long personId; + @NotBlank + private String text; + @NotBlank + private String time; + + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getPersonId() { + return personId; + } + + public void setPersonId(Long personId) { + this.personId = personId; + } + + public Long getFriendId() { + return friendId; + } + + public void setFriendId(Long friendId) { + this.friendId = friendId; + } + + public String getTime() { + return time; + } + + public void setTime(String time) { + this.time = time; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } +} diff --git a/2 lab/src/main/java/com/example/demo/messages/model/MessageEntity.java b/2 lab/src/main/java/com/example/demo/messages/model/MessageEntity.java new file mode 100644 index 0000000..be33c6d --- /dev/null +++ b/2 lab/src/main/java/com/example/demo/messages/model/MessageEntity.java @@ -0,0 +1,78 @@ +package com.example.demo.messages.model; + +import java.util.Date; +import java.util.Objects; + +import com.example.demo.core.model.BaseEntity; +import com.example.demo.users.model.UserEntity; + +public class MessageEntity extends BaseEntity { + private UserEntity person; + private UserEntity friend; + private Date time; + private String text; + + public MessageEntity() { + super(); + } + + public MessageEntity(Long id, UserEntity person, UserEntity friend, Date time, String text) { + super(id); + this.person = person; + this.friend = friend; + this.text = text; + this.time = time; + } + + public UserEntity getFriend() { + return friend; + } + + public void setFriend(UserEntity friend) { + this.friend = friend; + } + + public UserEntity getPerson() { + return person; + } + + public void setPerson(UserEntity person) { + this.person = person; + } + + public Date getTime() { + return time; + } + + public void setTime(Date time) { + this.time = time; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + + @Override + public int hashCode() { + return Objects.hash(id, friend, person, time, text); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null || getClass() != obj.getClass()) + return false; + final MessageEntity other = (MessageEntity) obj; + return Objects.equals(other.getId(), id) + && Objects.equals(other.getPerson(), person) + && Objects.equals(other.getFriend(), friend) + && Objects.equals(other.getText(), text) + && Objects.equals(other.getTime(), time); + } + +} diff --git a/2 lab/src/main/java/com/example/demo/messages/repository/MessageRepository.java b/2 lab/src/main/java/com/example/demo/messages/repository/MessageRepository.java new file mode 100644 index 0000000..be78ad3 --- /dev/null +++ b/2 lab/src/main/java/com/example/demo/messages/repository/MessageRepository.java @@ -0,0 +1,10 @@ +package com.example.demo.messages.repository; + +import org.springframework.stereotype.Repository; + +import com.example.demo.core.repository.MapRepository; +import com.example.demo.messages.model.MessageEntity; + +@Repository +public class MessageRepository extends MapRepository { +} diff --git a/2 lab/src/main/java/com/example/demo/messages/service/MessageService.java b/2 lab/src/main/java/com/example/demo/messages/service/MessageService.java new file mode 100644 index 0000000..c5a07af --- /dev/null +++ b/2 lab/src/main/java/com/example/demo/messages/service/MessageService.java @@ -0,0 +1,43 @@ +package com.example.demo.messages.service; + +import java.util.List; +import java.util.Optional; + +import org.springframework.stereotype.Service; + +import com.example.demo.core.error.NotFoundException; +import com.example.demo.messages.model.MessageEntity; +import com.example.demo.messages.repository.MessageRepository; + +@Service +public class MessageService { + private final MessageRepository repository; + + public MessageService(MessageRepository repository) { + this.repository = repository; + } + + public List getAll() { + return repository.getAll(); + } + + public MessageEntity get(Long id) { + return Optional.ofNullable(repository.get(id)) + .orElseThrow(() -> new NotFoundException(id)); + } + + public MessageEntity create(MessageEntity entity) { + return repository.create(entity); + } + + public MessageEntity update(Long id, MessageEntity entity) { + final MessageEntity existsEntity = get(id); + existsEntity.setText(entity.getText()); + return repository.update(existsEntity); + } + + public MessageEntity delete(Long id) { + final MessageEntity existsEntity = get(id); + return repository.delete(existsEntity); + } +} diff --git a/2 lab/src/main/java/com/example/demo/posts/api/PostController.java b/2 lab/src/main/java/com/example/demo/posts/api/PostController.java new file mode 100644 index 0000000..f7ee74d --- /dev/null +++ b/2 lab/src/main/java/com/example/demo/posts/api/PostController.java @@ -0,0 +1,89 @@ +package com.example.demo.posts.api; + +import java.util.List; + +import org.modelmapper.ModelMapper; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.example.demo.core.convert; +import com.example.demo.core.configuration.Constants; +import com.example.demo.posts.model.PostEntity; +import com.example.demo.posts.service.PostService; +import com.example.demo.users.service.UserService; + +import jakarta.validation.Valid; + +@RestController +@RequestMapping(Constants.API_URL + "/post") +public class PostController { + private final UserService userService; + private final PostService postService; + private final ModelMapper modelMapper; + + public PostController(PostService postService, UserService userService, ModelMapper modelMapper) { + this.postService = postService; + this.userService = userService; + this.modelMapper = modelMapper; + } + + private PostDto toDto(PostEntity entity) { + var model = modelMapper.map(entity, PostDto.class); + convert time = new convert(); + model.setTime(time.dateToString(entity.getTime())); + return model; + } + + private PostEntity toEntity(PostDto dto) { + final PostEntity entity = modelMapper.map(dto, PostEntity.class); + entity.setPerson(userService.get(dto.getPersonId())); + return entity; + + } + + @GetMapping + public List getAll() { + return postService.getAll().stream().map(this::toDto).toList(); + } + + @GetMapping("/{id}") + public PostDto get(@PathVariable(name = "id") Long id) { + return toDto(postService.get(id)); + } + + @PostMapping + public PostDto create(@RequestBody @Valid PostDto dto) { + return toDto(postService.create(toEntity(dto))); + } + + @PostMapping("/like/{id}") + public PostDto addLike(@PathVariable(name = "id") Long id) { + return toDto(postService.addLike(id)); + } + + @DeleteMapping("/like/{id}") + public PostDto removeLike(@PathVariable(name = "id") Long id) { + return toDto(postService.removeLiked(id)); + } + + @PostMapping("/view/{id}") + public PostDto addView(@PathVariable(name = "id") Long id) { + return toDto(postService.addView(id)); + } + + @PutMapping("/{id}") + public PostDto update(@PathVariable(name = "id") Long id, @RequestBody PostDto dto) { + return toDto(postService.update(id, toEntity(dto))); + } + + @DeleteMapping("/{id}") + public PostDto delete(@PathVariable(name = "id") Long id) { + return toDto(postService.delete(id)); + } +} diff --git a/2 lab/src/main/java/com/example/demo/posts/api/PostDto.java b/2 lab/src/main/java/com/example/demo/posts/api/PostDto.java new file mode 100644 index 0000000..5892709 --- /dev/null +++ b/2 lab/src/main/java/com/example/demo/posts/api/PostDto.java @@ -0,0 +1,73 @@ +package com.example.demo.posts.api; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; + +public class PostDto { + private Long id; + @NotBlank + @Min(1) + private Long personId; + @NotBlank + private String time; + @NotBlank + private String text; + @NotNull + @Min(0) + private Long viewed; + @NotNull + @Min(0) + private Long liked; + + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getPersonId() { + return personId; + } + + public void setPersonId(Long personId) { + this.personId = personId; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + + public String getTime() { + return time; + } + + public void setTime(String time) { + this.time = time; + } + + public Long getViewed() { + return viewed; + } + + public void setViewed(Long viewed) { + this.viewed = viewed; + } + + public Long getLiked() { + return liked; + } + + public void setLiked(Long liked) { + this.liked = liked; + } +} diff --git a/2 lab/src/main/java/com/example/demo/posts/model/PostEntity.java b/2 lab/src/main/java/com/example/demo/posts/model/PostEntity.java new file mode 100644 index 0000000..09e5360 --- /dev/null +++ b/2 lab/src/main/java/com/example/demo/posts/model/PostEntity.java @@ -0,0 +1,89 @@ +package com.example.demo.posts.model; + +import java.util.Date; +import java.util.Objects; + +import com.example.demo.core.model.BaseEntity; +import com.example.demo.users.model.UserEntity; + +public class PostEntity extends BaseEntity { + private UserEntity person; + private Date time; + private String text; + private Long viewed; + private Long liked; + + public PostEntity() { + super(); + } + + public PostEntity(Long id, UserEntity person, Date time, String text, Long viewed, Long liked) { + super(id); + this.person = person; + this.text = text; + this.time = time; + this.viewed = viewed; + this.liked = liked; + } + + public UserEntity getPerson() { + return person; + } + + public void setPerson(UserEntity person) { + this.person = person; + } + + public Date getTime() { + return time; + } + + public void setTime(Date time) { + this.time = time; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + + public Long getViewed() { + return viewed; + } + + public void setViewed(Long viewed) { + this.viewed = viewed; + } + + public Long getLiked() { + return liked; + } + + public void setLiked(Long liked) { + this.liked = liked; + } + + @Override + public int hashCode() { + return Objects.hash(id, person, time, text, viewed, liked); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null || getClass() != obj.getClass()) + return false; + final PostEntity other = (PostEntity) obj; + return Objects.equals(other.getId(), id) + && Objects.equals(other.getPerson(), person) + && Objects.equals(other.getText(), text) + && Objects.equals(other.getTime(), time) + && Objects.equals(other.getLiked(), liked) + && Objects.equals(other.getViewed(), viewed); + } + +} diff --git a/2 lab/src/main/java/com/example/demo/posts/repository/PostRepository.java b/2 lab/src/main/java/com/example/demo/posts/repository/PostRepository.java new file mode 100644 index 0000000..9b3f24e --- /dev/null +++ b/2 lab/src/main/java/com/example/demo/posts/repository/PostRepository.java @@ -0,0 +1,10 @@ +package com.example.demo.posts.repository; + +import org.springframework.stereotype.Repository; + +import com.example.demo.core.repository.MapRepository; +import com.example.demo.posts.model.PostEntity; + +@Repository +public class PostRepository extends MapRepository { +} diff --git a/2 lab/src/main/java/com/example/demo/posts/service/PostService.java b/2 lab/src/main/java/com/example/demo/posts/service/PostService.java new file mode 100644 index 0000000..3070a3b --- /dev/null +++ b/2 lab/src/main/java/com/example/demo/posts/service/PostService.java @@ -0,0 +1,61 @@ +package com.example.demo.posts.service; + +import java.util.List; +import java.util.Optional; + +import org.springframework.stereotype.Service; + +import com.example.demo.core.error.NotFoundException; +import com.example.demo.posts.model.PostEntity; +import com.example.demo.posts.repository.PostRepository; + +@Service +public class PostService { + private final PostRepository repository; + + public PostService(PostRepository repository) { + this.repository = repository; + } + + public List getAll() { + return repository.getAll(); + } + + public PostEntity get(Long id) { + return Optional.ofNullable(repository.get(id)) + .orElseThrow(() -> new NotFoundException(id)); + } + + public PostEntity create(PostEntity entity) { + return repository.create(entity); + } + + public PostEntity addLike(Long id) { + final PostEntity existsEntity = get(id); + existsEntity.setLiked((existsEntity.getLiked() + 1)); + return repository.update(existsEntity); + } + + public PostEntity removeLiked(Long id) { + final PostEntity existsEntity = get(id); + existsEntity.setLiked((existsEntity.getLiked() - 1)); + return repository.update(existsEntity); + } + + public PostEntity addView(Long id) { + final PostEntity existsEntity = get(id); + existsEntity.setViewed((existsEntity.getViewed() + 1)); + return repository.update(existsEntity); + } + + public PostEntity update(Long id, PostEntity entity) { + final PostEntity existsEntity = get(id); + existsEntity.setText(entity.getText()); + return repository.update(existsEntity); + } + + public PostEntity delete(Long id) { + final PostEntity existsEntity = get(id); + return repository.delete(existsEntity); + } +} diff --git a/2 lab/src/main/java/com/example/demo/users/api/UserController.java b/2 lab/src/main/java/com/example/demo/users/api/UserController.java new file mode 100644 index 0000000..14e9d29 --- /dev/null +++ b/2 lab/src/main/java/com/example/demo/users/api/UserController.java @@ -0,0 +1,82 @@ +package com.example.demo.users.api; + +import java.util.List; + +import org.modelmapper.ModelMapper; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.example.demo.core.configuration.Constants; +import com.example.demo.users.model.UserEntity; +import com.example.demo.users.service.UserService; + +import jakarta.validation.Valid; + +@RestController +@RequestMapping(Constants.API_URL + "/user") +public class UserController { + private final UserService userService; + private final ModelMapper modelMapper; + + public UserController(UserService userService, ModelMapper modelMapper) { + this.userService = userService; + this.modelMapper = modelMapper; + } + + private UserDto toDto(UserEntity entity) { + var model = modelMapper.map(entity, UserDto.class); + if (entity.getFriends() != null) { + for (UserEntity user : entity.getFriends()) { + model.addFriendId(user.getId()); + } + } + return model; + + } + + private UserEntity toEntity(UserDto dto) { + return modelMapper.map(dto, UserEntity.class); + } + + @GetMapping + public List getAll() { + return userService.getAll().stream().map(this::toDto).toList(); + } + + @GetMapping("/{id}") + public UserDto get(@PathVariable(name = "id") Long id) { + return toDto(userService.get(id)); + } + + @PostMapping + public UserDto create(@RequestBody @Valid UserDto dto) { + return toDto(userService.create(toEntity(dto))); + } + + @PostMapping("/friends/{id}") + public UserDto addFriend(@PathVariable(name = "id") Long id, @RequestBody UserDto dto) { + return toDto(userService.addFriend(id, toEntity(dto))); + } + + @DeleteMapping("/friends/{id}") + public UserDto removeFriend(@PathVariable(name = "id") Long id, @RequestBody UserDto dto) { + return toDto(userService.removeFriend(id, toEntity(dto))); + } + + @PutMapping("/{id}") + public UserDto update(@PathVariable(name = "id") Long id, @RequestBody UserDto dto) { + return toDto(userService.update(id, toEntity(dto))); + } + + @DeleteMapping("/{id}") + public UserDto delete(@PathVariable(name = "id") Long id) { + return toDto(userService.delete(id)); + } + +} diff --git a/2 lab/src/main/java/com/example/demo/users/api/UserDto.java b/2 lab/src/main/java/com/example/demo/users/api/UserDto.java new file mode 100644 index 0000000..434375a --- /dev/null +++ b/2 lab/src/main/java/com/example/demo/users/api/UserDto.java @@ -0,0 +1,65 @@ +package com.example.demo.users.api; + +import java.util.ArrayList; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.List; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; + +public class UserDto { + private Long id; + @NotNull + @Min(1) + private String nickname; + @NotBlank + private String email; + @NotBlank + private String password; + private List friendsId = new ArrayList<>(); + + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getNickname() { + return nickname; + } + + public void setNickname(String nickname) { + this.nickname = nickname; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + public List getFriendsId() { + return friendsId; + } + + public void addFriendId(long Id) { + friendsId.add(Id); + } + +} diff --git a/2 lab/src/main/java/com/example/demo/users/model/UserEntity.java b/2 lab/src/main/java/com/example/demo/users/model/UserEntity.java new file mode 100644 index 0000000..216077a --- /dev/null +++ b/2 lab/src/main/java/com/example/demo/users/model/UserEntity.java @@ -0,0 +1,76 @@ +package com.example.demo.users.model; + +import java.util.ArrayList; +import java.util.Objects; +import java.util.List; +import com.example.demo.core.model.BaseEntity; + +public class UserEntity extends BaseEntity { + private String nickname; + private String email; + private String password; + private List friends = new ArrayList<>(); + + public UserEntity() { + super(); + } + + public UserEntity(Long id, String nickname, String email, String password, List friends) { + super(id); + this.nickname = nickname; + this.email = email; + this.password = password; + this.friends = friends; + } + + public String getNickname() { + return nickname; + } + + public void setNickname(String nickname) { + this.nickname = nickname; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public List getFriends() { + return friends; + } + + public void setFriends(List friends) { + this.friends = friends; + } + + @Override + public int hashCode() { + return Objects.hash(id, nickname, email, password, friends); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null || getClass() != obj.getClass()) + return false; + final UserEntity other = (UserEntity) obj; + return Objects.equals(other.getId(), id) + && Objects.equals(other.getFriends(), friends) + && Objects.equals(other.getNickname(), nickname) + && Objects.equals(other.getEmail(), email) + && Objects.equals(other.getPassword(), password); + } +} diff --git a/2 lab/src/main/java/com/example/demo/users/repository/UserRepository.java b/2 lab/src/main/java/com/example/demo/users/repository/UserRepository.java new file mode 100644 index 0000000..fa4b654 --- /dev/null +++ b/2 lab/src/main/java/com/example/demo/users/repository/UserRepository.java @@ -0,0 +1,10 @@ +package com.example.demo.users.repository; + +import org.springframework.stereotype.Repository; + +import com.example.demo.core.repository.MapRepository; +import com.example.demo.users.model.UserEntity; + +@Repository +public class UserRepository extends MapRepository { +} diff --git a/2 lab/src/main/java/com/example/demo/users/service/UserService.java b/2 lab/src/main/java/com/example/demo/users/service/UserService.java new file mode 100644 index 0000000..012c03e --- /dev/null +++ b/2 lab/src/main/java/com/example/demo/users/service/UserService.java @@ -0,0 +1,57 @@ +package com.example.demo.users.service; + +import java.util.List; +import java.util.Optional; + +import org.springframework.stereotype.Service; + +import com.example.demo.core.error.NotFoundException; +import com.example.demo.users.model.UserEntity; +import com.example.demo.users.repository.UserRepository; + +@Service +public class UserService { + private final UserRepository repository; + + public UserService(UserRepository repository) { + this.repository = repository; + } + + public List getAll() { + return repository.getAll(); + } + + public UserEntity get(Long id) { + return Optional.ofNullable(repository.get(id)) + .orElseThrow(() -> new NotFoundException(id)); + } + + public UserEntity create(UserEntity entity) { + return repository.create(entity); + } + + public UserEntity addFriend(Long id, UserEntity entity) { + final UserEntity existsEntity = get(id); + existsEntity.getFriends().add(entity); + return repository.update(existsEntity); + } + + public UserEntity removeFriend(Long id, UserEntity entity) { + final UserEntity existsEntity = get(id); + existsEntity.getFriends().remove(entity); + return repository.update(existsEntity); + } + + public UserEntity update(Long id, UserEntity entity) { + final UserEntity existsEntity = get(id); + existsEntity.setNickname(entity.getNickname()); + existsEntity.setEmail(entity.getEmail()); + existsEntity.setPassword(entity.getPassword()); + return repository.update(existsEntity); + } + + public UserEntity delete(Long id) { + final UserEntity existsEntity = get(id); + return repository.delete(existsEntity); + } +} diff --git a/2 lab/src/main/resources/application.properties b/2 lab/src/main/resources/application.properties new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/2 lab/src/main/resources/application.properties @@ -0,0 +1 @@ + diff --git a/2 lab/src/test/java/com/example/demo/CommentServiceTests.java b/2 lab/src/test/java/com/example/demo/CommentServiceTests.java new file mode 100644 index 0000000..6aa5c6a --- /dev/null +++ b/2 lab/src/test/java/com/example/demo/CommentServiceTests.java @@ -0,0 +1,92 @@ +package com.example.demo; + +import java.util.Date; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import com.example.demo.comments.model.CommentEntity; +import com.example.demo.comments.service.CommentService; +import com.example.demo.core.error.NotFoundException; +import com.example.demo.users.model.UserEntity; +import com.example.demo.posts.model.PostEntity; +import com.example.demo.users.service.UserService; +import com.example.demo.posts.service.PostService;; + +@SpringBootTest +@TestMethodOrder(OrderAnnotation.class) +class CommentServiceTests { + @Autowired + private UserService userService; + @Autowired + private PostService postService; + @Autowired + private CommentService commentService; + + @Test + void getTest() { + Assertions.assertThrows(NotFoundException.class, () -> commentService.get(0L)); + } + + @Test + @Order(1) + void createTest() { + var user1 = userService.create(new UserEntity(null, "sardq", "as2@asd", "asdsd", null)); + var user2 = userService.create(new UserEntity(null, "eafqr", "aasd@asd", "1234r", null)); + var user3 = userService.create(new UserEntity(null, "asf", "aaasfsfs@asd", "332a", null)); + var post1 = postService.create(new PostEntity(null, user1, new Date(), "test", + Long.valueOf(0), Long.valueOf(0))); + var post2 = postService + .create(new PostEntity(null, user3, new Date(), "asfgczv", Long.valueOf(20), + Long.valueOf(15))); + var post3 = postService.create(new PostEntity(null, user2, new Date(), "asvbsfd", Long.valueOf(30), + Long.valueOf(3))); + commentService.create(new CommentEntity(null, user1, post2, new Date(), "my comment")); + commentService.create(new CommentEntity(null, user3, post1, new Date(), "my comment2")); + final var last = commentService + .create(new CommentEntity(null, user2, post3, new Date(), "my comment3")); + Assertions.assertEquals(3, commentService.getAll().size()); + Assertions.assertEquals(last, commentService.get(3L)); + } + + @Test + @Order(2) + void updateTest() { + final String test = "TEST"; + final CommentEntity entity = commentService.get(3L); + final String oldText = entity.getText(); + var user1 = userService.create(new UserEntity(null, "sardq", "as2@asd", "asdsd", null)); + var post1 = postService.create(new PostEntity(null, user1, + new Date(), "test", + Long.valueOf(0), Long.valueOf(0))); + + final CommentEntity newEntity = commentService.update(3L, + new CommentEntity(1L, user1, post1, new Date(), test)); + Assertions.assertEquals(3, commentService.getAll().size()); + Assertions.assertEquals(newEntity, commentService.get(3L)); + Assertions.assertEquals(test, newEntity.getText()); + Assertions.assertNotEquals(oldText, newEntity.getText()); + } + + @Test + @Order(3) + void deleteTest() { + commentService.delete(3L); + Assertions.assertEquals(2, commentService.getAll().size()); + final CommentEntity last = commentService.get(2L); + Assertions.assertEquals(2L, last.getId()); + final var user1 = userService.create(new UserEntity(null, "sardq", "as2@asd", "asdsd", null)); + final var post1 = postService.create(new PostEntity(null, user1, + new Date(), "test", + Long.valueOf(0), Long.valueOf(0))); + final CommentEntity newEntity = commentService.create( + new CommentEntity(null, user1, post1, new Date(), "jgdssd")); + Assertions.assertEquals(3, commentService.getAll().size()); + Assertions.assertEquals(4L, newEntity.getId()); + } +} diff --git a/2 lab/src/test/java/com/example/demo/MessageServiceTests.java b/2 lab/src/test/java/com/example/demo/MessageServiceTests.java new file mode 100644 index 0000000..741de13 --- /dev/null +++ b/2 lab/src/test/java/com/example/demo/MessageServiceTests.java @@ -0,0 +1,77 @@ +package com.example.demo; + +import java.util.Date; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import com.example.demo.core.error.NotFoundException; +import com.example.demo.messages.model.MessageEntity; +import com.example.demo.messages.service.MessageService; +import com.example.demo.users.model.UserEntity; +import com.example.demo.users.service.UserService; + +@SpringBootTest +@TestMethodOrder(OrderAnnotation.class) +class MessageServiceTests { + @Autowired + private UserService userService; + @Autowired + private MessageService messageService; + + @Test + void getTest() { + Assertions.assertThrows(NotFoundException.class, () -> messageService.get(0L)); + } + + @Test + @Order(1) + void createTest() { + var user1 = userService.create(new UserEntity(null, "sardq", "as2@asd", "asdsd", null)); + var user2 = userService.create(new UserEntity(null, "eafqr", "aasd@asd", "1234r", null)); + var user3 = userService.create(new UserEntity(null, "asf", "aaasfsfs@asd", "332a", null)); + messageService.create(new MessageEntity(null, user1, user2, new Date(), "test")); + messageService.create(new MessageEntity(null, user3, user2, new Date(), "asfgczv")); + final MessageEntity last = messageService + .create(new MessageEntity(null, user2, user1, new Date(), "asvbsfd")); + Assertions.assertEquals(3, messageService.getAll().size()); + Assertions.assertEquals(last, messageService.get(3L)); + } + + @Test + @Order(2) + void updateTest() { + final String test = "TEST"; + final MessageEntity entity = messageService.get(3L); + final String oldText = entity.getText(); + final MessageEntity newEntity = messageService.update(3L, + new MessageEntity(1L, + new UserEntity(null, "safsaf", "aasd@asd", "qazq", null), + new UserEntity(null, "safnkkjaslf", "safsaf@asd", "2qhf", null), + new Date(), test)); + Assertions.assertEquals(3, messageService.getAll().size()); + Assertions.assertEquals(newEntity, messageService.get(3L)); + Assertions.assertEquals(test, newEntity.getText()); + Assertions.assertNotEquals(oldText, newEntity.getText()); + } + + @Test + @Order(3) + void deleteTest() { + messageService.delete(3L); + Assertions.assertEquals(2, messageService.getAll().size()); + final MessageEntity last = messageService.get(2L); + Assertions.assertEquals(2L, last.getId()); + var user1 = userService.create(new UserEntity(null, "sardq", "as2@asd", "asdsd", null)); + var user2 = userService.create(new UserEntity(null, "sasfardq", "asfg@asd", "asfsaf", null)); + final MessageEntity newEntity = messageService.create( + new MessageEntity(null, user1, user2, new Date(), "test")); + Assertions.assertEquals(3, messageService.getAll().size()); + Assertions.assertEquals(4L, newEntity.getId()); + } +} diff --git a/2 lab/src/test/java/com/example/demo/PostServiceTests.java b/2 lab/src/test/java/com/example/demo/PostServiceTests.java new file mode 100644 index 0000000..80425f7 --- /dev/null +++ b/2 lab/src/test/java/com/example/demo/PostServiceTests.java @@ -0,0 +1,81 @@ +package com.example.demo; + +import java.util.Date; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import com.example.demo.core.error.NotFoundException; +import com.example.demo.users.model.UserEntity; +import com.example.demo.posts.model.PostEntity; +import com.example.demo.users.service.UserService; +import com.example.demo.posts.service.PostService;; + +@SpringBootTest +@TestMethodOrder(OrderAnnotation.class) +class PostServiceTests { + @Autowired + private UserService userService; + @Autowired + private PostService postService; + + @Test + void getTest() { + Assertions.assertThrows(NotFoundException.class, () -> postService.get(0L)); + } + + @Test + @Order(1) + void createTest() { + var user1 = userService.create(new UserEntity(null, "sardq", "as2@asd", "asdsd", null)); + var user2 = userService.create(new UserEntity(null, "eafqr", "aasd@asd", "1234r", null)); + var user3 = userService.create(new UserEntity(null, "asf", "aaasfsfs@asd", "332a", null)); + postService.create(new PostEntity(null, user1, + new Date(), "test", + Long.valueOf(0), Long.valueOf(0))); + postService.create(new PostEntity(null, user3, new Date(), "asfgczv", Long.valueOf(20), Long.valueOf(15))); + final PostEntity last = postService + .create(new PostEntity(null, user2, + new Date(), "asvbsfd", Long.valueOf(30), + Long.valueOf(3))); + postService.addLike(1L); + postService.addView(1L); + Assertions.assertEquals(3, postService.getAll().size()); + Assertions.assertEquals(last, postService.get(3L)); + } + + @Test + @Order(2) + void updateTest() { + final String test = "TEST"; + final PostEntity entity = postService.get(3L); + final String oldText = entity.getText(); + final PostEntity newEntity = postService.update(3L, + new PostEntity(1L, new UserEntity(1L, "safsaf", "aasd@asd", "qazq", null), + new Date(), test, Long.valueOf(30), Long.valueOf(3))); + Assertions.assertEquals(3, postService.getAll().size()); + Assertions.assertEquals(newEntity, postService.get(3L)); + Assertions.assertEquals(test, newEntity.getText()); + Assertions.assertNotEquals(oldText, newEntity.getText()); + } + + @Test + @Order(3) + void deleteTest() { + postService.delete(3L); + Assertions.assertEquals(2, postService.getAll().size()); + final PostEntity last = postService.get(2L); + postService.removeLiked(1L); + Assertions.assertEquals(2L, last.getId()); + var user1 = userService.create(new UserEntity(null, "sardq", "as2@asd", "asdsd", null)); + final PostEntity newEntity = postService.create( + new PostEntity(null, user1, new Date(), "test", Long.valueOf(0), Long.valueOf(0))); + Assertions.assertEquals(3, postService.getAll().size()); + Assertions.assertEquals(4L, newEntity.getId()); + } +} diff --git a/2 lab/src/test/java/com/example/demo/UserServiceTests.java b/2 lab/src/test/java/com/example/demo/UserServiceTests.java new file mode 100644 index 0000000..83d5453 --- /dev/null +++ b/2 lab/src/test/java/com/example/demo/UserServiceTests.java @@ -0,0 +1,68 @@ +package com.example.demo; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import com.example.demo.core.error.NotFoundException; +import com.example.demo.users.model.UserEntity; +import com.example.demo.users.service.UserService; + +@SpringBootTest +@TestMethodOrder(OrderAnnotation.class) +class UserServiceTests { + @Autowired + private UserService userService; + + @Test + void getTest() { + Assertions.assertThrows(NotFoundException.class, () -> userService.get(0L)); + } + + @Test + @Order(1) + void createTest() { + List friends = new ArrayList(); + friends.add(new UserEntity(2L, "eafqr", "aasd@asd", "1234r", new ArrayList())); + userService.create(new UserEntity(1L, "sardq", "as2@asd", "asdsd", friends)); + userService.create(new UserEntity(2L, "eafqr", "aasd@asd", "1234r", new ArrayList())); + final UserEntity last = userService.create(new UserEntity(3L, "fgwrewr", "sf123@asd", "zxvb", + new ArrayList())); + userService.addFriend(1L, last); + Assertions.assertEquals(3, userService.getAll().size()); + Assertions.assertEquals(last, userService.get(3L)); + } + + @Test + @Order(2) + void updateTest() { + final String test = "TEST"; + final UserEntity entity = userService.get(3L); + final String oldName = entity.getNickname(); + final UserEntity newEntity = userService.update(3L, new UserEntity(1L, test, "aasd@asd", "qazq", null)); + Assertions.assertEquals(3, userService.getAll().size()); + Assertions.assertEquals(newEntity, userService.get(3L)); + Assertions.assertEquals(test, newEntity.getNickname()); + Assertions.assertNotEquals(oldName, newEntity.getNickname()); + } + + @Test + @Order(3) + void deleteTest() { + userService.delete(3L); + Assertions.assertEquals(2, userService.getAll().size()); + final UserEntity last = userService.get(2L); + userService.removeFriend(2L, last); + Assertions.assertEquals(2L, last.getId()); + final UserEntity newEntity = userService.create(new UserEntity(null, "asfzxv", "aasd@asd", "qwtg", null)); + Assertions.assertEquals(3, userService.getAll().size()); + Assertions.assertEquals(4L, newEntity.getId()); + } +} diff --git a/3 lab/.gitignore b/3 lab/.gitignore new file mode 100644 index 0000000..546ecee --- /dev/null +++ b/3 lab/.gitignore @@ -0,0 +1,36 @@ +HELP.md +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +data.*.db \ No newline at end of file diff --git a/3 lab/.vscode/extensions.json b/3 lab/.vscode/extensions.json new file mode 100644 index 0000000..42cf79d --- /dev/null +++ b/3 lab/.vscode/extensions.json @@ -0,0 +1,12 @@ +{ + "recommendations": [ + // fronted + "AndersEAndersen.html-class-suggestions", + "dbaeumer.vscode-eslint", + // backend + "vscjava.vscode-java-pack", + "vmware.vscode-boot-dev-pack", + "vscjava.vscode-gradle", + "redhat.vscode-xml" + ] +} \ No newline at end of file diff --git a/3 lab/.vscode/launch.json b/3 lab/.vscode/launch.json new file mode 100644 index 0000000..a45a9b7 --- /dev/null +++ b/3 lab/.vscode/launch.json @@ -0,0 +1,14 @@ +{ + "configurations": [ + { + "type": "java", + "name": "Demo", + "request": "launch", + "cwd": "${workspaceFolder}", + "mainClass": "com.example.demo.DemoApplication", + "projectName": "lec3", + "args": "--populate", + "envFile": "${workspaceFolder}/.env" + } + ] +} \ No newline at end of file diff --git a/3 lab/.vscode/settings.json b/3 lab/.vscode/settings.json new file mode 100644 index 0000000..eab3db6 --- /dev/null +++ b/3 lab/.vscode/settings.json @@ -0,0 +1,21 @@ +{ + "editor.tabSize": 4, + "editor.detectIndentation": false, + "editor.insertSpaces": true, + "editor.formatOnPaste": true, + "editor.formatOnSave": true, + "editor.formatOnType": false, + "java.compile.nullAnalysis.mode": "disabled", + "java.configuration.updateBuildConfiguration": "automatic", + "[java]": { + "editor.pasteAs.enabled": false, + }, + "gradle.nestedProjects": true, + "java.saveActions.organizeImports": true, + "java.dependency.packagePresentation": "hierarchical", + "spring-boot.ls.problem.boot2.JAVA_CONSTRUCTOR_PARAMETER_INJECTION": "WARNING", + "spring.initializr.defaultLanguage": "Java", + "java.format.settings.url": ".vscode/eclipse-formatter.xml", + "java.project.explorer.showNonJavaResources": true, + "java.codeGeneration.hashCodeEquals.useJava7Objects": true, +} \ No newline at end of file diff --git a/3 lab/build.gradle b/3 lab/build.gradle new file mode 100644 index 0000000..1f5a14c --- /dev/null +++ b/3 lab/build.gradle @@ -0,0 +1,43 @@ +plugins { + id 'java' + id 'org.springframework.boot' version '3.2.4' + id 'io.spring.dependency-management' version '1.1.4' +} + +group = 'com.example' +version = '0.0.1-SNAPSHOT' + +defaultTasks 'bootRun' + +jar { + enabled = false +} + +bootJar { + archiveFileName = String.format('%s-%s.jar', rootProject.name, version) +} + +assert System.properties['java.specification.version'] == '17' || '21' +java { + sourceCompatibility = '17' +} + +repositories { + mavenCentral() +} + +dependencies { + implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springframework.boot:spring-boot-starter-validation' + implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.3.0' + implementation 'org.modelmapper:modelmapper:3.2.0' + + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + implementation 'com.h2database:h2:2.2.224' + + testImplementation 'org.springframework.boot:spring-boot-starter-test' +} + +tasks.named('test') { + useJUnitPlatform() +} diff --git a/3 lab/gradle/wrapper/gradle-wrapper.jar b/3 lab/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..d64cd4917707c1f8861d8cb53dd15194d4248596 GIT binary patch literal 43462 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 literal 0 HcmV?d00001 diff --git a/3 lab/gradle/wrapper/gradle-wrapper.properties b/3 lab/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..1af9e09 --- /dev/null +++ b/3 lab/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/3 lab/gradlew b/3 lab/gradlew new file mode 100644 index 0000000..1aa94a4 --- /dev/null +++ b/3 lab/gradlew @@ -0,0 +1,249 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/3 lab/gradlew.bat b/3 lab/gradlew.bat new file mode 100644 index 0000000..93e3f59 --- /dev/null +++ b/3 lab/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/3 lab/readme.md b/3 lab/readme.md new file mode 100644 index 0000000..6eea3e5 --- /dev/null +++ b/3 lab/readme.md @@ -0,0 +1,18 @@ +Swagger UI: \ +http://localhost:8080/swagger-ui/index.html + +H2 Console: \ +http://localhost:8080/h2-console + +JDBC URL: jdbc:h2:file:./data \ +User Name: sa \ +Password: password + +Почитать: + +- Односторонние и двусторонние связи https://www.baeldung.com/jpa-hibernate-associations +- Getters и Setters для двусторонних связей https://en.wikibooks.org/wiki/Java_Persistence/OneToMany#Getters_and_Setters +- Многие-ко-многим с доп. атрибутами https://thorben-janssen.com/hibernate-tip-many-to-many-association-with-additional-attributes/ +- Многие-ко-многим с доп. атрибутами https://www.baeldung.com/jpa-many-to-many +- Каскадное удаление сущностей со связями многие-ко-многим https://www.baeldung.com/jpa-remove-entity-many-to-many +- Выбор типа коллекции для отношений вида ко-многим в JPA https://thorben-janssen.com/association-mappings-bag-list-set/ diff --git a/3 lab/settings.gradle b/3 lab/settings.gradle new file mode 100644 index 0000000..0a383dd --- /dev/null +++ b/3 lab/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'demo' diff --git a/3 lab/src/main/java/com/example/demo/DemoApplication.java b/3 lab/src/main/java/com/example/demo/DemoApplication.java new file mode 100644 index 0000000..baeef45 --- /dev/null +++ b/3 lab/src/main/java/com/example/demo/DemoApplication.java @@ -0,0 +1,87 @@ +package com.example.demo; + +import java.sql.Time; +import java.util.ArrayList; +import java.util.Objects; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.CommandLineRunner; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +import java.util.List; + +import com.example.demo.comments.model.CommentEntity; +import com.example.demo.comments.service.CommentService; +import com.example.demo.messages.model.MessageEntity; +import com.example.demo.messages.service.MessageService; +import com.example.demo.posts.model.PostEntity; +import com.example.demo.posts.service.PostService; +import com.example.demo.users.model.UserEntity; +import com.example.demo.users.service.UserService; + +@SpringBootApplication +public class DemoApplication implements CommandLineRunner { + private final Logger log = LoggerFactory.getLogger(DemoApplication.class); + + private final UserService userService; + private final PostService postService; + private final CommentService commentService; + private final MessageService messageService; + + public DemoApplication(UserService userService, PostService postService, CommentService commentService, + MessageService messageService) { + this.userService = userService; + this.postService = postService; + this.commentService = commentService; + this.messageService = messageService; + } + + public static void main(String[] args) { + SpringApplication.run(DemoApplication.class, args); + } + + @Override + public void run(String... args) throws Exception { + if (args.length > 0 && Objects.equals("--populate", args[0])) { + + log.info("Create default items values"); + final List blank = new ArrayList(); + final var user1 = userService.create(new UserEntity("sardq", "aasd@asd", "asdsd", null)); + final var user2 = userService.create(new UserEntity("sardasdq", "aasdsad@asd", "123", blank)); + final List friends = new ArrayList<>(); + friends.add(user1); + friends.add(user2); + userService.addFriend(user1.getId(), user2); + + final var user3 = userService.create(new UserEntity("sxzcvardq", "aaxsd@asd", "asfgd", friends)); + final var user4 = userService.create(new UserEntity("sarbvljdq", "aacbxcsd@asd", "zxvb", blank)); + log.info("Create default posts values"); + final var post1 = postService.create( + new PostEntity(user1, new Time(System.currentTimeMillis()), "test", Long.valueOf(0), + Long.valueOf(0))); + final var post2 = postService.create( + new PostEntity(user3, new Time(System.currentTimeMillis()), "test", Long.valueOf(11), + Long.valueOf(2))); + final var post3 = postService.create( + new PostEntity(user4, new Time(System.currentTimeMillis()), "test", Long.valueOf(40), + Long.valueOf(15))); + + log.info("Create default comments values"); + commentService + .create(new CommentEntity(user1, post2, new Time(System.currentTimeMillis()), "my comment")); + commentService + .create(new CommentEntity(user3, post1, new Time(System.currentTimeMillis()), "my comment2")); + commentService + .create(new CommentEntity(user4, post3, new Time(System.currentTimeMillis()), "my comment3")); + + log.info("Create default messages values"); + messageService.create(new MessageEntity(user1, user2, new Time(System.currentTimeMillis()), "hello")); + messageService + .create(new MessageEntity(user1, user2, new Time(System.currentTimeMillis()), "how are you")); + messageService + .create(new MessageEntity(user2, user1, new Time(System.currentTimeMillis()), "I'm ok")); + } + } +} diff --git a/3 lab/src/main/java/com/example/demo/comments/api/CommentController.java b/3 lab/src/main/java/com/example/demo/comments/api/CommentController.java new file mode 100644 index 0000000..f0806e5 --- /dev/null +++ b/3 lab/src/main/java/com/example/demo/comments/api/CommentController.java @@ -0,0 +1,81 @@ +package com.example.demo.comments.api; + +import org.modelmapper.ModelMapper; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import com.example.demo.comments.model.CommentEntity; +import com.example.demo.comments.service.CommentService; +import com.example.demo.core.convert; +import com.example.demo.core.api.PageDto; +import com.example.demo.core.api.PageDtoMapper; +import com.example.demo.core.configuration.Constants; +import com.example.demo.posts.service.PostService; +import com.example.demo.users.service.UserService; + +import jakarta.validation.Valid; + +@RestController +@RequestMapping(Constants.API_URL + "/comment") +public class CommentController { + private final UserService userService; + private final PostService postService; + private final CommentService commentService; + private final ModelMapper modelMapper; + + public CommentController(CommentService commentService, UserService userService, PostService postService, + ModelMapper modelMapper) { + this.userService = userService; + this.commentService = commentService; + this.postService = postService; + this.modelMapper = modelMapper; + } + + private CommentDto toDto(CommentEntity entity) { + var model = modelMapper.map(entity, CommentDto.class); + convert time = new convert(); + model.setTime(time.dateToString(entity.getTime())); + return model; + } + + private CommentEntity toEntity(CommentDto dto) { + final CommentEntity entity = modelMapper.map(dto, CommentEntity.class); + entity.setperson(userService.get(dto.getUserId())); + entity.setPost(postService.get(dto.getPostId())); + return entity; + } + + @GetMapping + public PageDto getAll( + @RequestParam(name = "page", defaultValue = "0") int page, + @RequestParam(name = "size", defaultValue = Constants.DEFAULT_PAGE_SIZE) int size) { + return PageDtoMapper.toDto(commentService.getAll(page, size), this::toDto); + } + + @GetMapping("/{id}") + public CommentDto get(@PathVariable(name = "id") Long id) { + return toDto(commentService.get(id)); + } + + @PostMapping + public CommentDto create(@RequestBody @Valid CommentDto dto) { + return toDto(commentService.create(toEntity(dto))); + } + + @PutMapping("/{id}") + public CommentDto update(@PathVariable(name = "id") Long id, @RequestBody CommentDto dto) { + return toDto(commentService.update(id, toEntity(dto))); + } + + @DeleteMapping("/{id}") + public CommentDto delete(@PathVariable(name = "id") Long id) { + return toDto(commentService.delete(id)); + } +} diff --git a/3 lab/src/main/java/com/example/demo/comments/api/CommentDto.java b/3 lab/src/main/java/com/example/demo/comments/api/CommentDto.java new file mode 100644 index 0000000..51a35f7 --- /dev/null +++ b/3 lab/src/main/java/com/example/demo/comments/api/CommentDto.java @@ -0,0 +1,62 @@ +package com.example.demo.comments.api; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; + +public class CommentDto { + private Long id; + @NotNull + @Min(1) + private Long userId; + @NotBlank + private String text; + @NotNull + @Min(1) + private Long postId; + @NotBlank + private String time; + + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getUserId() { + return userId; + } + + public void setUserId(Long userId) { + this.userId = userId; + } + + public Long getPostId() { + return postId; + } + + public void setPostId(Long postId) { + this.postId = postId; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + + public String getTime() { + return time; + } + + public void setTime(String time) { + this.time = time; + } +} diff --git a/3 lab/src/main/java/com/example/demo/comments/model/CommentEntity.java b/3 lab/src/main/java/com/example/demo/comments/model/CommentEntity.java new file mode 100644 index 0000000..b276ea1 --- /dev/null +++ b/3 lab/src/main/java/com/example/demo/comments/model/CommentEntity.java @@ -0,0 +1,90 @@ +package com.example.demo.comments.model; + +import java.util.Date; +import java.util.Objects; + +import com.example.demo.core.model.BaseEntity; +import com.example.demo.posts.model.PostEntity; +import com.example.demo.users.model.UserEntity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; + +@Entity +@Table(name = "comments") +public class CommentEntity extends BaseEntity { + @ManyToOne + @JoinColumn(name = "personEntity", nullable = false) + private UserEntity person; + @ManyToOne + @JoinColumn(name = "postEntity", nullable = false) + private PostEntity post; + @Column(nullable = false) + private Date time; + @Column(length = 100) + private String text; + + public CommentEntity() { + } + + public CommentEntity(UserEntity person, PostEntity post, Date time, String text) { + this.person = person; + this.post = post; + this.time = time; + this.text = text; + } + + public UserEntity getperson() { + return person; + } + + public void setperson(UserEntity person) { + this.person = person; + } + + public PostEntity getPost() { + return post; + } + + public void setPost(PostEntity post) { + this.post = post; + } + + public Date getTime() { + return time; + } + + public void setTime(Date time) { + this.time = time; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + + @Override + public int hashCode() { + return Objects.hash(id, person, post, text, time); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null || getClass() != obj.getClass()) + return false; + final CommentEntity other = (CommentEntity) obj; + return Objects.equals(other.getId(), id) + && Objects.equals(other.getperson(), person) + && Objects.equals(other.getPost(), post) + && Objects.equals(other.getText(), text) + && Objects.equals(other.getTime(), time); + } +} diff --git a/3 lab/src/main/java/com/example/demo/comments/repository/CommentRepository.java b/3 lab/src/main/java/com/example/demo/comments/repository/CommentRepository.java new file mode 100644 index 0000000..6d10803 --- /dev/null +++ b/3 lab/src/main/java/com/example/demo/comments/repository/CommentRepository.java @@ -0,0 +1,14 @@ +package com.example.demo.comments.repository; + +import java.util.Optional; + +import org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.PagingAndSortingRepository; + +import com.example.demo.comments.model.CommentEntity; + +public interface CommentRepository + extends CrudRepository, PagingAndSortingRepository { + + Optional findById(Long Id); +} \ No newline at end of file diff --git a/3 lab/src/main/java/com/example/demo/comments/service/CommentService.java b/3 lab/src/main/java/com/example/demo/comments/service/CommentService.java new file mode 100644 index 0000000..f17d41f --- /dev/null +++ b/3 lab/src/main/java/com/example/demo/comments/service/CommentService.java @@ -0,0 +1,63 @@ +package com.example.demo.comments.service; + +import java.util.List; +import java.util.stream.StreamSupport; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.example.demo.comments.model.CommentEntity; +import com.example.demo.comments.repository.CommentRepository; +import com.example.demo.core.error.NotFoundException; + +@Service +public class CommentService { + private final CommentRepository repository; + + public CommentService(CommentRepository repository) { + this.repository = repository; + } + + @Transactional(readOnly = true) + public List getAll() { + return StreamSupport.stream(repository.findAll().spliterator(), false).toList(); + + } + + @Transactional(readOnly = true) + public Page getAll(int page, int size) { + return repository.findAll(PageRequest.of(page, size)); + } + + @Transactional(readOnly = true) + public CommentEntity get(Long id) { + return repository.findById(id) + .orElseThrow(() -> new NotFoundException(CommentEntity.class, id)); + } + + @Transactional + public CommentEntity create(CommentEntity entity) { + if (entity == null) { + throw new IllegalArgumentException("Entity is null"); + } + repository.save(entity); + return repository.save(entity); + } + + @Transactional + public CommentEntity update(Long id, CommentEntity entity) { + final CommentEntity existsEntity = get(id); + existsEntity.setText(entity.getText()); + repository.save(existsEntity); + return existsEntity; + } + + @Transactional + public CommentEntity delete(Long id) { + final CommentEntity existsEntity = get(id); + repository.delete(existsEntity); + return existsEntity; + } +} diff --git a/3 lab/src/main/java/com/example/demo/core/api/PageDto.java b/3 lab/src/main/java/com/example/demo/core/api/PageDto.java new file mode 100644 index 0000000..4cae429 --- /dev/null +++ b/3 lab/src/main/java/com/example/demo/core/api/PageDto.java @@ -0,0 +1,97 @@ +package com.example.demo.core.api; + +import java.util.ArrayList; +import java.util.List; + +public class PageDto { + private List items = new ArrayList<>(); + private int itemsCount; + private int currentPage; + private int currentSize; + private int totalPages; + private long totalItems; + private boolean isFirst; + private boolean isLast; + private boolean hasNext; + private boolean hasPrevious; + + public List getItems() { + return items; + } + + public void setItems(List items) { + this.items = items; + } + + public int getItemsCount() { + return itemsCount; + } + + public void setItemsCount(int itemsCount) { + this.itemsCount = itemsCount; + } + + public int getCurrentPage() { + return currentPage; + } + + public void setCurrentPage(int currentPage) { + this.currentPage = currentPage; + } + + public int getCurrentSize() { + return currentSize; + } + + public void setCurrentSize(int currentSize) { + this.currentSize = currentSize; + } + + public int getTotalPages() { + return totalPages; + } + + public void setTotalPages(int totalPages) { + this.totalPages = totalPages; + } + + public long getTotalItems() { + return totalItems; + } + + public void setTotalItems(long totalItems) { + this.totalItems = totalItems; + } + + public boolean isFirst() { + return isFirst; + } + + public void setFirst(boolean isFirst) { + this.isFirst = isFirst; + } + + public boolean isLast() { + return isLast; + } + + public void setLast(boolean isLast) { + this.isLast = isLast; + } + + public boolean isHasNext() { + return hasNext; + } + + public void setHasNext(boolean hasNext) { + this.hasNext = hasNext; + } + + public boolean isHasPrevious() { + return hasPrevious; + } + + public void setHasPrevious(boolean hasPrevious) { + this.hasPrevious = hasPrevious; + } +} diff --git a/3 lab/src/main/java/com/example/demo/core/api/PageDtoMapper.java b/3 lab/src/main/java/com/example/demo/core/api/PageDtoMapper.java new file mode 100644 index 0000000..e8d3dd0 --- /dev/null +++ b/3 lab/src/main/java/com/example/demo/core/api/PageDtoMapper.java @@ -0,0 +1,25 @@ +package com.example.demo.core.api; + +import java.util.function.Function; + +import org.springframework.data.domain.Page; + +public class PageDtoMapper { + private PageDtoMapper() { + } + + public static PageDto toDto(Page page, Function mapper) { + final PageDto dto = new PageDto<>(); + dto.setItems(page.getContent().stream().map(mapper::apply).toList()); + dto.setItemsCount(page.getNumberOfElements()); + dto.setCurrentPage(page.getNumber()); + dto.setCurrentSize(page.getSize()); + dto.setTotalPages(page.getTotalPages()); + dto.setTotalItems(page.getTotalElements()); + dto.setFirst(page.isFirst()); + dto.setLast(page.isLast()); + dto.setHasNext(page.hasNext()); + dto.setHasPrevious(page.hasPrevious()); + return dto; + } +} diff --git a/3 lab/src/main/java/com/example/demo/core/configuration/Constants.java b/3 lab/src/main/java/com/example/demo/core/configuration/Constants.java new file mode 100644 index 0000000..4f3264d --- /dev/null +++ b/3 lab/src/main/java/com/example/demo/core/configuration/Constants.java @@ -0,0 +1,10 @@ +package com.example.demo.core.configuration; + +public class Constants { + public static final String API_URL = "/api/1.0"; + public static final String SEQUENCE_NAME = "hibernate_sequence"; + public static final String DEFAULT_PAGE_SIZE = "5"; + + private Constants() { + } +} diff --git a/3 lab/src/main/java/com/example/demo/core/configuration/MapperConfiguration.java b/3 lab/src/main/java/com/example/demo/core/configuration/MapperConfiguration.java new file mode 100644 index 0000000..a5ad6f3 --- /dev/null +++ b/3 lab/src/main/java/com/example/demo/core/configuration/MapperConfiguration.java @@ -0,0 +1,13 @@ +package com.example.demo.core.configuration; + +import org.modelmapper.ModelMapper; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class MapperConfiguration { + @Bean + ModelMapper modelMapper() { + return new ModelMapper(); + } +} diff --git a/3 lab/src/main/java/com/example/demo/core/configuration/WebConfiguration.java b/3 lab/src/main/java/com/example/demo/core/configuration/WebConfiguration.java new file mode 100644 index 0000000..762e85a --- /dev/null +++ b/3 lab/src/main/java/com/example/demo/core/configuration/WebConfiguration.java @@ -0,0 +1,15 @@ +package com.example.demo.core.configuration; + +import org.springframework.context.annotation.Configuration; +import org.springframework.lang.NonNull; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +public class WebConfiguration implements WebMvcConfigurer { + @Override + public void addCorsMappings(@NonNull CorsRegistry registry) { + registry.addMapping("/**") + .allowedMethods("GET", "POST", "PUT", "DELETE"); + } +} diff --git a/3 lab/src/main/java/com/example/demo/core/convert.java b/3 lab/src/main/java/com/example/demo/core/convert.java new file mode 100644 index 0000000..5973126 --- /dev/null +++ b/3 lab/src/main/java/com/example/demo/core/convert.java @@ -0,0 +1,17 @@ +package com.example.demo.core; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; + +public class convert { + public Date stringToDate(String string) throws Exception { + DateFormat format = new SimpleDateFormat("HH:mm:ss"); + return (Date) format.parse(string); + } + + public String dateToString(Date date) { + DateFormat dateFormat = new SimpleDateFormat("dd.MM.YYYY hh:mm:ss"); + return dateFormat.format(date); + } +} diff --git a/3 lab/src/main/java/com/example/demo/core/error/NotFoundException.java b/3 lab/src/main/java/com/example/demo/core/error/NotFoundException.java new file mode 100644 index 0000000..a61d118 --- /dev/null +++ b/3 lab/src/main/java/com/example/demo/core/error/NotFoundException.java @@ -0,0 +1,7 @@ +package com.example.demo.core.error; + +public class NotFoundException extends RuntimeException { + public NotFoundException(Class clazz, Long id) { + super(String.format("%s with id [%s] is not found or not exists", clazz.getSimpleName(), id)); + } +} diff --git a/3 lab/src/main/java/com/example/demo/core/model/BaseEntity.java b/3 lab/src/main/java/com/example/demo/core/model/BaseEntity.java new file mode 100644 index 0000000..6ba4476 --- /dev/null +++ b/3 lab/src/main/java/com/example/demo/core/model/BaseEntity.java @@ -0,0 +1,32 @@ +package com.example.demo.core.model; + +import com.example.demo.core.configuration.Constants; + +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.MappedSuperclass; +import jakarta.persistence.SequenceGenerator; + +@MappedSuperclass +public abstract class BaseEntity { + @Id + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = Constants.SEQUENCE_NAME) + @SequenceGenerator(name = Constants.SEQUENCE_NAME, sequenceName = Constants.SEQUENCE_NAME, allocationSize = 1) + protected Long id; + + protected BaseEntity() { + } + + protected BaseEntity(Long id) { + this.id = id; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } +} diff --git a/3 lab/src/main/java/com/example/demo/messages/api/MessageController.java b/3 lab/src/main/java/com/example/demo/messages/api/MessageController.java new file mode 100644 index 0000000..e9ea648 --- /dev/null +++ b/3 lab/src/main/java/com/example/demo/messages/api/MessageController.java @@ -0,0 +1,79 @@ +package com.example.demo.messages.api; + +import org.modelmapper.ModelMapper; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import com.example.demo.core.convert; +import com.example.demo.core.api.PageDto; +import com.example.demo.core.api.PageDtoMapper; +import com.example.demo.core.configuration.Constants; +import com.example.demo.messages.model.MessageEntity; +import com.example.demo.messages.service.MessageService; +import com.example.demo.users.service.UserService; + +import jakarta.validation.Valid; + +@RestController +@RequestMapping(Constants.API_URL + "/message") +public class MessageController { + private final UserService userService; + private final MessageService messageService; + private final ModelMapper modelMapper; + + public MessageController(MessageService messageService, UserService userService, ModelMapper modelMapper) { + this.messageService = messageService; + this.userService = userService; + this.modelMapper = modelMapper; + } + + private MessageDto toDto(MessageEntity entity) { + var model = modelMapper.map(entity, MessageDto.class); + convert time = new convert(); + model.setTime(time.dateToString(entity.getTime())); + return model; + } + + private MessageEntity toEntity(MessageDto dto) { + final MessageEntity entity = modelMapper.map(dto, MessageEntity.class); + entity.setFriend(userService.get(dto.getFriendId())); + entity.setPerson(userService.get(dto.getPersonId())); + return entity; + + } + + @GetMapping + public PageDto getAll( + @RequestParam(name = "personId", defaultValue = "0") Long personId, + @RequestParam(name = "page", defaultValue = "0") int page, + @RequestParam(name = "size", defaultValue = Constants.DEFAULT_PAGE_SIZE) int size) { + return PageDtoMapper.toDto(messageService.getAll(personId, page, size), this::toDto); + } + + @GetMapping("/{id}") + public MessageDto get(@PathVariable(name = "id") Long id) { + return toDto(messageService.get(id)); + } + + @PostMapping + public MessageDto create(@RequestBody @Valid MessageDto dto) { + return toDto(messageService.create(toEntity(dto))); + } + + @PutMapping("/{id}") + public MessageDto update(@PathVariable(name = "id") Long id, @RequestBody MessageDto dto) { + return toDto(messageService.update(id, toEntity(dto))); + } + + @DeleteMapping("/{id}") + public MessageDto delete(@PathVariable(name = "id") Long id) { + return toDto(messageService.delete(id)); + } +} diff --git a/3 lab/src/main/java/com/example/demo/messages/api/MessageDto.java b/3 lab/src/main/java/com/example/demo/messages/api/MessageDto.java new file mode 100644 index 0000000..d92bce1 --- /dev/null +++ b/3 lab/src/main/java/com/example/demo/messages/api/MessageDto.java @@ -0,0 +1,62 @@ +package com.example.demo.messages.api; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; + +public class MessageDto { + private Long id; + @NotNull + @Min(1) + private Long friendId; + @NotNull + @Min(1) + private Long personId; + @NotBlank + private String text; + @NotBlank + private String time; + + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getPersonId() { + return personId; + } + + public void setPersonId(Long personId) { + this.personId = personId; + } + + public Long getFriendId() { + return friendId; + } + + public void setFriendId(Long friendId) { + this.friendId = friendId; + } + + public String getTime() { + return time; + } + + public void setTime(String time) { + this.time = time; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } +} diff --git a/3 lab/src/main/java/com/example/demo/messages/model/MessageEntity.java b/3 lab/src/main/java/com/example/demo/messages/model/MessageEntity.java new file mode 100644 index 0000000..c70c405 --- /dev/null +++ b/3 lab/src/main/java/com/example/demo/messages/model/MessageEntity.java @@ -0,0 +1,91 @@ +package com.example.demo.messages.model; + +import java.util.Date; +import java.util.Objects; + +import com.example.demo.core.model.BaseEntity; +import com.example.demo.users.model.UserEntity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; + +@Entity +@Table(name = "messages") +public class MessageEntity extends BaseEntity { + + @ManyToOne + @JoinColumn(name = "userEntity", nullable = false) + private UserEntity person; + @ManyToOne + @JoinColumn(name = "dialogFriend", nullable = false) + private UserEntity friend; + @Column(nullable = false) + private Date time; + @Column(length = 100) + private String text; + + public MessageEntity() { + } + + public MessageEntity(UserEntity person, UserEntity friend, Date time, String text) { + this.person = person; + this.friend = friend; + this.text = text; + this.time = time; + } + + public UserEntity getFriend() { + return friend; + } + + public void setFriend(UserEntity friend) { + this.friend = friend; + } + + public UserEntity getPerson() { + return person; + } + + public void setPerson(UserEntity person) { + this.person = person; + } + + public Date getTime() { + return time; + } + + public void setTime(Date time) { + this.time = time; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + + @Override + public int hashCode() { + return Objects.hash(id, friend, person, time, text); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null || getClass() != obj.getClass()) + return false; + final MessageEntity other = (MessageEntity) obj; + return Objects.equals(other.getId(), id) + && Objects.equals(other.getPerson(), person) + && Objects.equals(other.getFriend(), friend) + && Objects.equals(other.getText(), text) + && Objects.equals(other.getTime(), time); + } + +} diff --git a/3 lab/src/main/java/com/example/demo/messages/repository/MessageRepository.java b/3 lab/src/main/java/com/example/demo/messages/repository/MessageRepository.java new file mode 100644 index 0000000..066864b --- /dev/null +++ b/3 lab/src/main/java/com/example/demo/messages/repository/MessageRepository.java @@ -0,0 +1,21 @@ +package com.example.demo.messages.repository; + +import java.util.List; +import java.util.Optional; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.PagingAndSortingRepository; + +import com.example.demo.messages.model.MessageEntity; + +public interface MessageRepository + extends CrudRepository, PagingAndSortingRepository { + List findByPersonId(Long personId); + + Page findByPersonId(Long personId, Pageable pageable); + + Optional findById(Long Id); + +} \ No newline at end of file diff --git a/3 lab/src/main/java/com/example/demo/messages/service/MessageService.java b/3 lab/src/main/java/com/example/demo/messages/service/MessageService.java new file mode 100644 index 0000000..b6eec15 --- /dev/null +++ b/3 lab/src/main/java/com/example/demo/messages/service/MessageService.java @@ -0,0 +1,72 @@ +package com.example.demo.messages.service; + +import java.util.List; +import java.util.stream.StreamSupport; + +import org.springframework.transaction.annotation.Transactional; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; + +import com.example.demo.core.error.NotFoundException; +import com.example.demo.messages.model.MessageEntity; +import com.example.demo.messages.repository.MessageRepository; + +@Service +public class MessageService { + private final MessageRepository repository; + + public MessageService(MessageRepository repository) { + this.repository = repository; + } + + @Transactional(readOnly = true) + public List getAll(long personId) { + if (personId <= 0L) { + return StreamSupport.stream(repository.findAll().spliterator(), false).toList(); + } else { + return repository.findByPersonId(personId); + } + + } + + @Transactional(readOnly = true) + public Page getAll(long personId, int page, int size) { + final Pageable pageRequest = PageRequest.of(page, size); + if (personId <= 0L) { + return repository.findAll(pageRequest); + } + return repository.findByPersonId(personId, pageRequest); + } + + @Transactional + public MessageEntity get(Long id) { + return repository.findById(id) + .orElseThrow(() -> new NotFoundException(MessageEntity.class, id)); + } + + @Transactional + public MessageEntity create(MessageEntity entity) { + if (entity == null) { + throw new IllegalArgumentException("Entity is null"); + } + repository.save(entity); + return repository.save(entity); + } + + @Transactional + public MessageEntity update(Long id, MessageEntity entity) { + final MessageEntity existsEntity = get(id); + existsEntity.setText(entity.getText()); + repository.save(existsEntity); + return existsEntity; + } + + @Transactional + public MessageEntity delete(Long id) { + final MessageEntity existsEntity = get(id); + repository.delete(existsEntity); + return existsEntity; + } +} diff --git a/3 lab/src/main/java/com/example/demo/posts/api/PostController.java b/3 lab/src/main/java/com/example/demo/posts/api/PostController.java new file mode 100644 index 0000000..e00e620 --- /dev/null +++ b/3 lab/src/main/java/com/example/demo/posts/api/PostController.java @@ -0,0 +1,92 @@ +package com.example.demo.posts.api; + +import org.modelmapper.ModelMapper; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import com.example.demo.core.convert; +import com.example.demo.core.api.PageDto; +import com.example.demo.core.api.PageDtoMapper; +import com.example.demo.core.configuration.Constants; +import com.example.demo.posts.model.PostEntity; +import com.example.demo.posts.service.PostService; +import com.example.demo.users.service.UserService; + +import jakarta.validation.Valid; + +@RestController +@RequestMapping(Constants.API_URL + "/post") +public class PostController { + private final UserService userService; + private final PostService postService; + private final ModelMapper modelMapper; + + public PostController(PostService postService, UserService userService, ModelMapper modelMapper) { + this.postService = postService; + this.userService = userService; + this.modelMapper = modelMapper; + } + + private PostDto toDto(PostEntity entity) { + var model = modelMapper.map(entity, PostDto.class); + convert time = new convert(); + model.setTime(time.dateToString(entity.getTime())); + return model; + } + + private PostEntity toEntity(PostDto dto) { + final PostEntity entity = modelMapper.map(dto, PostEntity.class); + entity.setPerson(userService.get(dto.getPersonId())); + return entity; + + } + + @GetMapping + public PageDto getAll( + @RequestParam(name = "page", defaultValue = "0") int page, + @RequestParam(name = "size", defaultValue = Constants.DEFAULT_PAGE_SIZE) int size) { + return PageDtoMapper.toDto(postService.getAll(page, size), this::toDto); + } + + @GetMapping("/{id}") + public PostDto get(@PathVariable(name = "id") Long id) { + return toDto(postService.get(id)); + } + + @PostMapping + public PostDto create(@RequestBody @Valid PostDto dto) { + return toDto(postService.create(toEntity(dto))); + } + + @PostMapping("/like/{id}") + public PostDto addLike(@PathVariable(name = "id") Long id) { + return toDto(postService.addLike(id)); + } + + @DeleteMapping("/like/{id}") + public PostDto removeLike(@PathVariable(name = "id") Long id) { + return toDto(postService.removeLiked(id)); + } + + @PostMapping("/view/{id}") + public PostDto addView(@PathVariable(name = "id") Long id) { + return toDto(postService.addView(id)); + } + + @PutMapping("/{id}") + public PostDto update(@PathVariable(name = "id") Long id, @RequestBody PostDto dto) { + return toDto(postService.update(id, toEntity(dto))); + } + + @DeleteMapping("/{id}") + public PostDto delete(@PathVariable(name = "id") Long id) { + return toDto(postService.delete(id)); + } +} diff --git a/3 lab/src/main/java/com/example/demo/posts/api/PostDto.java b/3 lab/src/main/java/com/example/demo/posts/api/PostDto.java new file mode 100644 index 0000000..2d1c9ec --- /dev/null +++ b/3 lab/src/main/java/com/example/demo/posts/api/PostDto.java @@ -0,0 +1,76 @@ +package com.example.demo.posts.api; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import com.fasterxml.jackson.annotation.JsonProperty.Access; + +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; + +public class PostDto { + @JsonProperty(access = Access.READ_ONLY) + private Long id; + @NotNull + @Min(1) + private Long personId; + @NotBlank + private String time; + @NotBlank + private String text; + @NotNull + @Min(0) + private Long viewed; + @NotNull + @Min(0) + private Long liked; + + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getPersonId() { + return personId; + } + + public void setPersonId(Long personId) { + this.personId = personId; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + + public String getTime() { + return time; + } + + public void setTime(String time) { + this.time = time; + } + + public Long getViewed() { + return viewed; + } + + public void setViewed(Long viewed) { + this.viewed = viewed; + } + + public Long getLiked() { + return liked; + } + + public void setLiked(Long liked) { + this.liked = liked; + } +} diff --git a/3 lab/src/main/java/com/example/demo/posts/model/PostEntity.java b/3 lab/src/main/java/com/example/demo/posts/model/PostEntity.java new file mode 100644 index 0000000..ce2e7c6 --- /dev/null +++ b/3 lab/src/main/java/com/example/demo/posts/model/PostEntity.java @@ -0,0 +1,102 @@ +package com.example.demo.posts.model; + +import java.util.Date; +import java.util.Objects; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; + +import com.example.demo.core.model.BaseEntity; +import com.example.demo.users.model.UserEntity; + +@Entity +@Table(name = "posts") +public class PostEntity extends BaseEntity { + @ManyToOne + @JoinColumn(name = "user_Id", nullable = false) + private UserEntity person; + @Column(nullable = false) + private Date time; + @Column(length = 100) + private String text; + @Column + private Long viewed; + @Column + private Long liked; + + public PostEntity() { + super(); + } + + public PostEntity(UserEntity person, Date time, String text, Long viewed, Long liked) { + this.person = person; + this.text = text; + this.time = time; + this.viewed = viewed; + this.liked = liked; + } + + public UserEntity getPerson() { + return person; + } + + public void setPerson(UserEntity person) { + this.person = person; + } + + public Date getTime() { + return time; + } + + public void setTime(Date time) { + this.time = time; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + + public Long getViewed() { + return viewed; + } + + public void setViewed(Long viewed) { + this.viewed = viewed; + } + + public Long getLiked() { + return liked; + } + + public void setLiked(Long liked) { + this.liked = liked; + } + + @Override + public int hashCode() { + return Objects.hash(id, person, time, text, viewed, liked); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null || getClass() != obj.getClass()) + return false; + final PostEntity other = (PostEntity) obj; + return Objects.equals(other.getId(), id) + && Objects.equals(other.getPerson(), person) + && Objects.equals(other.getText(), text) + && Objects.equals(other.getTime(), time) + && Objects.equals(other.getLiked(), liked) + && Objects.equals(other.getViewed(), viewed); + } + +} diff --git a/3 lab/src/main/java/com/example/demo/posts/repository/PostRepository.java b/3 lab/src/main/java/com/example/demo/posts/repository/PostRepository.java new file mode 100644 index 0000000..6f962f3 --- /dev/null +++ b/3 lab/src/main/java/com/example/demo/posts/repository/PostRepository.java @@ -0,0 +1,14 @@ +package com.example.demo.posts.repository; + +import java.util.Optional; +import org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.PagingAndSortingRepository; + +import com.example.demo.posts.model.PostEntity; + +public interface PostRepository + extends CrudRepository, PagingAndSortingRepository { + + Optional findById(Long Id); + +} \ No newline at end of file diff --git a/3 lab/src/main/java/com/example/demo/posts/service/PostService.java b/3 lab/src/main/java/com/example/demo/posts/service/PostService.java new file mode 100644 index 0000000..a5c369c --- /dev/null +++ b/3 lab/src/main/java/com/example/demo/posts/service/PostService.java @@ -0,0 +1,86 @@ +package com.example.demo.posts.service; + +import java.util.List; +import java.util.stream.StreamSupport; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.example.demo.core.error.NotFoundException; +import com.example.demo.posts.model.PostEntity; +import com.example.demo.posts.repository.PostRepository; + +@Service +public class PostService { + private final PostRepository repository; + + public PostService(PostRepository repository) { + this.repository = repository; + } + + @Transactional(readOnly = true) + public List getAll() { + return StreamSupport.stream(repository.findAll().spliterator(), false).toList(); + } + + @Transactional(readOnly = true) + public Page getAll(int page, int size) { + return repository.findAll(PageRequest.of(page, size)); + } + + @Transactional(readOnly = true) + public PostEntity get(Long id) { + return repository.findById(id) + .orElseThrow(() -> new NotFoundException(PostEntity.class, id)); + } + + @Transactional + public PostEntity create(PostEntity entity) { + if (entity == null) { + throw new IllegalArgumentException("Entity is null"); + } + repository.save(entity); + return repository.save(entity); + } + + @Transactional + public PostEntity addLike(Long id) { + final PostEntity existsEntity = get(id); + existsEntity.setLiked((existsEntity.getLiked() + 1)); + repository.save(existsEntity); + return existsEntity; + } + + @Transactional + public PostEntity removeLiked(Long id) { + final PostEntity existsEntity = get(id); + existsEntity.setLiked((existsEntity.getLiked() - 1)); + repository.save(existsEntity); + return existsEntity; + } + + @Transactional + public PostEntity addView(Long id) { + final PostEntity existsEntity = get(id); + existsEntity.setViewed((existsEntity.getViewed() + 1)); + repository.save(existsEntity); + return existsEntity; + } + + @Transactional + public PostEntity update(Long id, PostEntity entity) { + final PostEntity existsEntity = get(id); + existsEntity.setText(entity.getText()); + repository.save(existsEntity); + return existsEntity; + } + + @Transactional + public PostEntity delete(Long id) { + final PostEntity existsEntity = get(id); + repository.delete(existsEntity); + return existsEntity; + } +} diff --git a/3 lab/src/main/java/com/example/demo/users/api/UserController.java b/3 lab/src/main/java/com/example/demo/users/api/UserController.java new file mode 100644 index 0000000..54013dd --- /dev/null +++ b/3 lab/src/main/java/com/example/demo/users/api/UserController.java @@ -0,0 +1,94 @@ +package com.example.demo.users.api; + +import java.util.List; + +import org.modelmapper.ModelMapper; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.example.demo.core.configuration.Constants; +import com.example.demo.users.model.UserEntity; +import com.example.demo.users.model.UserTop; +import com.example.demo.users.service.UserService; + +import jakarta.validation.Valid; + +@RestController +@RequestMapping(Constants.API_URL + "/user") +public class UserController { + private final UserService userService; + private final ModelMapper modelMapper; + + public UserController(UserService userService, ModelMapper modelMapper) { + this.userService = userService; + this.modelMapper = modelMapper; + } + + private UserDto toDto(UserEntity entity) { + var model = modelMapper.map(entity, UserDto.class); + if (entity.getFriends() != null) { + for (UserEntity user : entity.getFriends()) { + model.addFriendId(user.getId()); + } + } + return model; + + } + + public UserTopDto toTopDto(UserTop user) { + return modelMapper.map(user, UserTopDto.class); + + } + + private UserEntity toEntity(UserDto dto) { + return modelMapper.map(dto, UserEntity.class); + } + + @GetMapping + public List getAll() { + return userService.getAll().stream().map(this::toDto).toList(); + } + + @GetMapping("/{id}") + public UserDto get(@PathVariable(name = "id") Long id) { + return toDto(userService.get(id)); + } + + @PostMapping + public UserDto create(@RequestBody @Valid UserDto dto) { + return toDto(userService.create(toEntity(dto))); + } + + @PostMapping("/friends/{id}") + public UserDto addFriend(@PathVariable(name = "id") Long id, @RequestBody UserDto dto) { + return toDto(userService.addFriend(id, toEntity(dto))); + } + + @DeleteMapping("/friends/{id}") + public UserDto removeFriend(@PathVariable(name = "id") Long id, @RequestBody UserDto dto) { + return toDto(userService.removeFriend(id, toEntity(dto))); + } + + @PutMapping("/{id}") + public UserDto update(@PathVariable(name = "id") Long id, @RequestBody UserDto dto) { + return toDto(userService.update(id, toEntity(dto))); + } + + @DeleteMapping("/{id}") + public UserDto delete(@PathVariable(name = "id") Long id) { + return toDto(userService.delete(id)); + } + + @GetMapping("/topFriends") + public List getMethod() { + return userService.getTop(0, 5).getContent().stream() + .map(this::toTopDto) + .toList(); + } +} diff --git a/3 lab/src/main/java/com/example/demo/users/api/UserDto.java b/3 lab/src/main/java/com/example/demo/users/api/UserDto.java new file mode 100644 index 0000000..0d8a088 --- /dev/null +++ b/3 lab/src/main/java/com/example/demo/users/api/UserDto.java @@ -0,0 +1,68 @@ +package com.example.demo.users.api; + +import java.util.ArrayList; +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonProperty.Access; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; + +public class UserDto { + @JsonProperty(access = Access.READ_ONLY) + private Long id; + @NotBlank + @Size(min = 3, max = 20) + private String nickname; + @NotBlank + @Size(min = 3, max = 20) + private String email; + @NotBlank + @Size(min = 3, max = 20) + private String password; + private List friendsId = new ArrayList<>(); + + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getNickname() { + return nickname; + } + + public void setNickname(String nickname) { + this.nickname = nickname; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + public List getFriendsId() { + return friendsId; + } + + public void addFriendId(long Id) { + friendsId.add(Id); + } + +} diff --git a/3 lab/src/main/java/com/example/demo/users/api/UserTopDto.java b/3 lab/src/main/java/com/example/demo/users/api/UserTopDto.java new file mode 100644 index 0000000..fe86cf7 --- /dev/null +++ b/3 lab/src/main/java/com/example/demo/users/api/UserTopDto.java @@ -0,0 +1,26 @@ +package com.example.demo.users.api; + +public class UserTopDto { + private String user; + private int count; + + public UserTopDto() { + } + + public String getUser() { + return user; + } + + public void setUser(String user) { + this.user = user; + } + + public int getCount() { + return count; + } + + public void setCount(int count) { + this.count = count; + } + +} diff --git a/3 lab/src/main/java/com/example/demo/users/model/UserEntity.java b/3 lab/src/main/java/com/example/demo/users/model/UserEntity.java new file mode 100644 index 0000000..a3173e0 --- /dev/null +++ b/3 lab/src/main/java/com/example/demo/users/model/UserEntity.java @@ -0,0 +1,92 @@ +package com.example.demo.users.model; + +import java.util.ArrayList; +import java.util.Objects; + +import java.util.List; +import com.example.demo.core.model.BaseEntity; + +import jakarta.persistence.CascadeType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.JoinTable; +import jakarta.persistence.ManyToMany; +import jakarta.persistence.Table; + +@Entity +@Table(name = "users") +public class UserEntity extends BaseEntity { + @Column(nullable = false, length = 20) + private String nickname; + @Column(nullable = false, unique = true, length = 20) + private String email; + @Column(nullable = false, length = 20) + private String password; + @ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.REFRESH) + @JoinTable(name = "user_friendship", joinColumns = @JoinColumn(name = "first_user_id", referencedColumnName = "Id", nullable = false), inverseJoinColumns = @JoinColumn(name = "second_user_id", referencedColumnName = "Id", nullable = false)) + private List friends = new ArrayList(); + + public UserEntity() { + } + + public UserEntity(String nickname, String email, String password, List friends) { + + this.nickname = nickname; + this.email = email; + this.password = password; + this.friends = friends; + } + + public String getNickname() { + return nickname; + } + + public void setNickname(String nickname) { + this.nickname = nickname; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public List getFriends() { + return friends; + } + + public void setFriends(List friends) { + this.friends = friends; + } + + @Override + public int hashCode() { + return Objects.hash(id, nickname, email, password, friends); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null || getClass() != obj.getClass()) + return false; + final UserEntity other = (UserEntity) obj; + return Objects.equals(other.getId(), id) + && Objects.equals(other.getFriends(), friends) + && Objects.equals(other.getNickname(), nickname) + && Objects.equals(other.getEmail(), email) + && Objects.equals(other.getPassword(), password); + } +} \ No newline at end of file diff --git a/3 lab/src/main/java/com/example/demo/users/model/UserTop.java b/3 lab/src/main/java/com/example/demo/users/model/UserTop.java new file mode 100644 index 0000000..8b8ade8 --- /dev/null +++ b/3 lab/src/main/java/com/example/demo/users/model/UserTop.java @@ -0,0 +1,7 @@ +package com.example.demo.users.model; + +public interface UserTop { + String getUser(); + + int getCount(); +} diff --git a/3 lab/src/main/java/com/example/demo/users/repository/UserRepository.java b/3 lab/src/main/java/com/example/demo/users/repository/UserRepository.java new file mode 100644 index 0000000..3b3f81c --- /dev/null +++ b/3 lab/src/main/java/com/example/demo/users/repository/UserRepository.java @@ -0,0 +1,26 @@ +package com.example.demo.users.repository; + +import java.util.Optional; +import java.util.List; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.CrudRepository; + +import com.example.demo.users.model.UserEntity; +import com.example.demo.users.model.UserTop; + +public interface UserRepository extends CrudRepository { + List findByNickname(String nickname); + + Optional findById(Long Id); + + Optional findByEmailIgnoreCase(String email); + + @Query("select " + + " u.nickname as user, coalesce(size(u.friends), 0) as count " + + "from UserEntity u " + + "order by size(u.friends) desc") + Page getFiveWithTopFriends(Pageable pageable); +} diff --git a/3 lab/src/main/java/com/example/demo/users/service/UserService.java b/3 lab/src/main/java/com/example/demo/users/service/UserService.java new file mode 100644 index 0000000..8dc0c03 --- /dev/null +++ b/3 lab/src/main/java/com/example/demo/users/service/UserService.java @@ -0,0 +1,95 @@ +package com.example.demo.users.service; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.StreamSupport; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.stereotype.Service; + +import com.example.demo.core.error.NotFoundException; +import com.example.demo.users.model.UserEntity; +import com.example.demo.users.model.UserTop; +import com.example.demo.users.repository.UserRepository; + +import org.springframework.transaction.annotation.Transactional; + +@Service +public class UserService { + private final UserRepository repository; + + public UserService(UserRepository repository) { + this.repository = repository; + } + + @Transactional(readOnly = true) + private void checkEmail(String email) { + if (repository.findByEmailIgnoreCase(email).isPresent()) { + throw new IllegalArgumentException( + String.format("User with email %s is already exists", email)); + } + } + + @Transactional(readOnly = true) + public List getAll() { + return StreamSupport.stream(repository.findAll().spliterator(), false).toList(); + } + + @Transactional(readOnly = true) + public UserEntity get(Long id) { + return repository.findById(id) + .orElseThrow(() -> new NotFoundException(UserEntity.class, id)); + } + + @Transactional + public UserEntity create(UserEntity entity) { + if (entity == null) { + throw new IllegalArgumentException("Entity is null"); + } + checkEmail(entity.getEmail()); + repository.save(entity); + return repository.save(entity); + } + + @Transactional + public UserEntity addFriend(Long id, UserEntity entity) { + final UserEntity existsEntity = get(id); + if (existsEntity.getFriends() == null) + existsEntity.setFriends(new ArrayList()); + existsEntity.getFriends().add(entity); + repository.save(existsEntity); + return existsEntity; + } + + @Transactional + public UserEntity removeFriend(Long id, UserEntity entity) { + final UserEntity existsEntity = get(id); + existsEntity.getFriends().remove(entity); + repository.save(existsEntity); + return existsEntity; + } + + @Transactional + public UserEntity update(Long id, UserEntity entity) { + final UserEntity existsEntity = get(id); + checkEmail(entity.getEmail()); + existsEntity.setNickname(entity.getNickname()); + existsEntity.setEmail(entity.getEmail()); + existsEntity.setPassword(entity.getPassword()); + repository.save(existsEntity); + return existsEntity; + } + + @Transactional + public UserEntity delete(Long id) { + final UserEntity existsEntity = get(id); + repository.delete(existsEntity); + return existsEntity; + } + + @Transactional(readOnly = true) + public Page getTop(int page, int size) { + return repository.getFiveWithTopFriends(PageRequest.of(page, size)); + } +} diff --git a/3 lab/src/main/resources/application.properties b/3 lab/src/main/resources/application.properties new file mode 100644 index 0000000..e670717 --- /dev/null +++ b/3 lab/src/main/resources/application.properties @@ -0,0 +1,21 @@ + +# Server +spring.main.banner-mode=off +server.port=8080 + +# Logger settings +# Available levels are: TRACE, DEBUG, INFO, WARN, ERROR, FATAL, OFF +logging.level.com.example.demo=DEBUG + +# JPA Settings +spring.datasource.url=jdbc:h2:file:./data +spring.datasource.username=sa +spring.datasource.password=password +spring.datasource.driver-class-name=org.h2.Driver +spring.jpa.hibernate.ddl-auto=create +spring.jpa.open-in-view=false +# spring.jpa.show-sql=true +# spring.jpa.properties.hibernate.format_sql=true + +# H2 console +spring.h2.console.enabled=true \ No newline at end of file diff --git a/3 lab/src/test/java/com/example/demo/CommentServiceTests.java b/3 lab/src/test/java/com/example/demo/CommentServiceTests.java new file mode 100644 index 0000000..9930469 --- /dev/null +++ b/3 lab/src/test/java/com/example/demo/CommentServiceTests.java @@ -0,0 +1,109 @@ +package com.example.demo; + +import java.util.Date; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.dao.DataIntegrityViolationException; + +import com.example.demo.comments.model.CommentEntity; +import com.example.demo.comments.service.CommentService; +import com.example.demo.core.error.NotFoundException; +import com.example.demo.users.model.UserEntity; +import com.example.demo.posts.model.PostEntity; +import com.example.demo.users.service.UserService; +import com.example.demo.posts.service.PostService;; + +@SpringBootTest +@TestMethodOrder(OrderAnnotation.class) +class CommentServiceTests { + @Autowired + private UserService userService; + @Autowired + private PostService postService; + @Autowired + private CommentService commentService; + + private CommentEntity comment; + + @Test + void createNullableTest() { + final CommentEntity nullableType = new CommentEntity(null, null, null, null); + Assertions.assertThrows(DataIntegrityViolationException.class, () -> commentService.create(nullableType)); + } + + @Test + void getTest() { + Assertions.assertThrows(NotFoundException.class, () -> commentService.get(0L)); + } + + @BeforeEach + void createData() { + removeData(); + + var user1 = userService.create(new UserEntity("sardq", "as2@asd", "asdsd", null)); + var user2 = userService.create(new UserEntity("eafqr", "aasd@asd", "1234r", null)); + var user3 = userService.create(new UserEntity("asf", "aaasfsfs@asd", "332a", null)); + var post1 = postService.create(new PostEntity(user1, new Date(), "test", + Long.valueOf(0), Long.valueOf(0))); + var post2 = postService + .create(new PostEntity(user3, new Date(), "asfgczv", Long.valueOf(20), + Long.valueOf(15))); + var post3 = postService.create(new PostEntity(user2, new Date(), "asvbsfd", Long.valueOf(30), + Long.valueOf(3))); + commentService.create(new CommentEntity(user1, post2, new Date(), "my comment")); + commentService.create(new CommentEntity(user3, post1, new Date(), "my comment2")); + comment = commentService + .create(new CommentEntity(user2, post3, new Date(), "my comment3")); + } + + @AfterEach + void removeData() { + commentService.getAll().forEach(item -> commentService.delete(item.getId())); + postService.getAll().forEach(item -> postService.delete(item.getId())); + userService.getAll().forEach(item -> userService.delete(item.getId())); + } + + @Test + void createTest() { + Assertions.assertEquals(3, commentService.getAll().size()); + } + + @Test + void updateTest() { + final String test = "TEST"; + final CommentEntity entity = commentService.get(comment.getId()); + final String oldText = entity.getText(); + var user1 = userService.create(new UserEntity("sardq", "asfa2@asd", "asdsd", null)); + var post1 = postService.create(new PostEntity(user1, + new Date(), "test", + Long.valueOf(0), Long.valueOf(0))); + + final CommentEntity newEntity = commentService.update(comment + .getId(), + new CommentEntity(user1, post1, new Date(), test)); + Assertions.assertEquals(3, commentService.getAll().size()); + Assertions.assertEquals(test, newEntity.getText()); + Assertions.assertNotEquals(oldText, newEntity.getText()); + } + + @Test + void deleteTest() { + commentService.delete(comment.getId()); + Assertions.assertEquals(2, commentService.getAll().size()); + + final var user1 = userService.create(new UserEntity("sardq", "asfa2@asd", "asdsd", null)); + final var post1 = postService.create(new PostEntity(user1, + new Date(), "test", + Long.valueOf(0), Long.valueOf(0))); + commentService.create( + new CommentEntity(user1, post1, new Date(), "jgdssd")); + Assertions.assertEquals(3, commentService.getAll().size()); + } +} diff --git a/3 lab/src/test/java/com/example/demo/MessageServiceTests.java b/3 lab/src/test/java/com/example/demo/MessageServiceTests.java new file mode 100644 index 0000000..633291d --- /dev/null +++ b/3 lab/src/test/java/com/example/demo/MessageServiceTests.java @@ -0,0 +1,97 @@ +package com.example.demo; + +import java.util.Date; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.dao.DataIntegrityViolationException; + +import com.example.demo.core.error.NotFoundException; +import com.example.demo.messages.model.MessageEntity; +import com.example.demo.messages.service.MessageService; +import com.example.demo.users.model.UserEntity; +import com.example.demo.users.service.UserService; + +@SpringBootTest +@TestMethodOrder(OrderAnnotation.class) +class MessageServiceTests { + @Autowired + private UserService userService; + @Autowired + private MessageService messageService; + + private MessageEntity message; + + @Test + void getTest() { + Assertions.assertThrows(NotFoundException.class, () -> messageService.get(0L)); + } + + @BeforeEach + void createData() { + removeData(); + + var user1 = userService.create(new UserEntity("sardq", "as2@asd", "asdsd", null)); + var user2 = userService.create(new UserEntity("eafqr", "aasd@asd", "1234r", null)); + var user3 = userService.create(new UserEntity("asf", "aaasfsfs@asd", "332a", null)); + messageService.create(new MessageEntity(user1, user2, new Date(), "test")); + messageService.create(new MessageEntity(user3, user2, new Date(), "asfgczv")); + message = messageService + .create(new MessageEntity(user2, user1, new Date(), "asvbsfd")); + } + + @AfterEach + void removeData() { + messageService.getAll(0L).forEach(item -> messageService.delete(item.getId())); + userService.getAll().forEach(item -> userService.delete(item.getId())); + + } + + @Test + void createNullableTest() { + final MessageEntity nullableType = new MessageEntity(null, null, null, null); + Assertions.assertThrows(DataIntegrityViolationException.class, () -> messageService.create(nullableType)); + } + + @Test + @Order(1) + void createTest() { + Assertions.assertEquals(3, messageService.getAll(0L).size()); + } + + @Test + @Order(2) + void updateTest() { + final String test = "TEST"; + final MessageEntity entity = messageService.get(message.getId()); + final String oldText = entity.getText(); + final MessageEntity newEntity = messageService.update(message.getId(), + new MessageEntity( + new UserEntity("safsaf", "afaa@asd", "qazq", null), + new UserEntity("safnkkjaslf", "afaafasd@asd", "2qhf", null), + new Date(), test)); + Assertions.assertEquals(3, messageService.getAll(0L).size()); + Assertions.assertEquals(test, newEntity.getText()); + Assertions.assertNotEquals(oldText, newEntity.getText()); + } + + @Test + @Order(3) + void deleteTest() { + messageService.delete(message.getId()); + Assertions.assertEquals(2, messageService.getAll(0L).size()); + + var user1 = userService.create(new UserEntity("sardq", "asafsd@asd", "asdsd", null)); + var user2 = userService.create(new UserEntity("sasfardq", "asfjsafcg@asd", "asfsaf", null)); + messageService.create( + new MessageEntity(user1, user2, new Date(), "test")); + Assertions.assertEquals(3, messageService.getAll(0L).size()); + } +} diff --git a/3 lab/src/test/java/com/example/demo/PostServiceTests.java b/3 lab/src/test/java/com/example/demo/PostServiceTests.java new file mode 100644 index 0000000..48df07d --- /dev/null +++ b/3 lab/src/test/java/com/example/demo/PostServiceTests.java @@ -0,0 +1,115 @@ +package com.example.demo; + +import java.util.Date; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.dao.DataIntegrityViolationException; + +import com.example.demo.core.error.NotFoundException; +import com.example.demo.users.model.UserEntity; +import com.example.demo.posts.model.PostEntity; +import com.example.demo.users.service.UserService; + +import jakarta.transaction.Transactional; + +import com.example.demo.posts.service.PostService;; + +@SpringBootTest +@TestMethodOrder(OrderAnnotation.class) +class PostServiceTests { + @Autowired + private UserService userService; + @Autowired + private PostService postService; + + private PostEntity post; + + @BeforeEach + void createData() { + removeData(); + + var user1 = userService.create(new UserEntity("sardq", "aqqq2@asd", "asdsd", null)); + var user2 = userService.create(new UserEntity("eafqr", "qqqq@asd", "1234r", null)); + var user3 = userService.create(new UserEntity("asf", "wwww@asd", "332a", null)); + postService.create(new PostEntity(user1, + new Date(), "test", + Long.valueOf(0), Long.valueOf(0))); + postService.create(new PostEntity(user3, new Date(), "asfgczv", Long.valueOf(20), Long.valueOf(15))); + post = postService + .create(new PostEntity(user2, + new Date(), "asvbsfd", Long.valueOf(0), + Long.valueOf(0))); + } + + @Test + void createNullableTest() { + final PostEntity nullableType = new PostEntity(null, null, null, null, null); + Assertions.assertThrows(DataIntegrityViolationException.class, () -> postService.create(nullableType)); + } + + @Test + @Transactional + void ViewLikeTest() { + postService.addLike(post.getId()); + postService.addLike(post.getId()); + postService.addLike(post.getId()); + Assertions.assertEquals(3, post.getLiked()); + postService.removeLiked(post.getId()); + postService.removeLiked(post.getId()); + Assertions.assertEquals(1, post.getLiked()); + postService.addView(post.getId()); + postService.addView(post.getId()); + Assertions.assertEquals(2, post.getViewed()); + + } + + @AfterEach + void removeData() { + postService.getAll().forEach(item -> postService.delete(item.getId())); + userService.getAll().forEach(item -> userService.delete(item.getId())); + } + + @Test + void getTest() { + Assertions.assertThrows(NotFoundException.class, () -> postService.get(0L)); + } + + @Test + @Order(1) + void createTest() { + + Assertions.assertEquals(3, postService.getAll().size()); + } + + @Test + @Order(2) + void updateTest() { + final String test = "TEST"; + final String oldText = post.getText(); + final PostEntity newEntity = postService.update(post.getId(), + new PostEntity(new UserEntity("safsaf", "eee@asd", "qazq", null), + new Date(), test, Long.valueOf(30), Long.valueOf(3))); + Assertions.assertEquals(3, postService.getAll().size()); + Assertions.assertEquals(test, newEntity.getText()); + Assertions.assertNotEquals(oldText, newEntity.getText()); + } + + @Test + @Order(3) + void deleteTest() { + postService.delete(post.getId()); + Assertions.assertEquals(2, postService.getAll().size()); + var user1 = userService.create(new UserEntity("sardq", "sssss@asd", "asdsd", null)); + postService.create( + new PostEntity(user1, new Date(), "test", Long.valueOf(0), Long.valueOf(0))); + Assertions.assertEquals(3, postService.getAll().size()); + } +} diff --git a/3 lab/src/test/java/com/example/demo/UserServiceTests.java b/3 lab/src/test/java/com/example/demo/UserServiceTests.java new file mode 100644 index 0000000..23a5c52 --- /dev/null +++ b/3 lab/src/test/java/com/example/demo/UserServiceTests.java @@ -0,0 +1,89 @@ +package com.example.demo; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.dao.DataIntegrityViolationException; + +import com.example.demo.core.error.NotFoundException; +import com.example.demo.users.model.UserEntity; +import com.example.demo.users.service.UserService; + +import jakarta.transaction.Transactional; + +@SpringBootTest(properties = "spring.main.lazy-initialization=true") +class UserServiceTests { + @Autowired + private UserService userService; + + private UserEntity user; + private UserEntity last; + + @BeforeEach + void createData() { + removeData(); + user = userService.create(new UserEntity("sardq", "test", "test", null)); + userService.create(new UserEntity("asd", "test2", "test1", null)); + last = userService.create(new UserEntity("asfkasf", "test3", "test2", null)); + } + + @AfterEach + void removeData() { + userService.getAll().forEach(item -> userService.delete(item.getId())); + } + + @Test + void getTest() { + Assertions.assertThrows(NotFoundException.class, () -> userService.get(0L)); + } + + @Test + void createTest() { + Assertions.assertEquals(3, userService.getAll().size()); + } + + @Test + void createNotUniqueTest() { + final UserEntity nonUniqueType = new UserEntity("asd", "test", "asdad", null); + Assertions.assertThrows(IllegalArgumentException.class, () -> userService.create(nonUniqueType)); + } + + @Test + @Transactional + void FriendsTest() { + userService.addFriend(user.getId(), last); + Assertions.assertEquals(1, user.getFriends().size()); + userService.removeFriend(user.getId(), last); + Assertions.assertEquals(0, user.getFriends().size()); + } + + @Test + void updateTest() { + final String test = "TEST"; + final UserEntity entity = userService.get(user.getId()); + final String oldName = entity.getNickname(); + final UserEntity newEntity = userService.update(user.getId(), new UserEntity(test, "aasd@asd", "qazq", null)); + Assertions.assertEquals(3, userService.getAll().size()); + Assertions.assertEquals(test, newEntity.getNickname()); + Assertions.assertNotEquals(oldName, newEntity.getNickname()); + } + + @Test + void createNullableTest() { + final UserEntity nullableType = new UserEntity(null, null, null, null); + Assertions.assertThrows(DataIntegrityViolationException.class, () -> userService.create(nullableType)); + } + + @Test + void deleteTest() { + userService.delete(last.getId()); + Assertions.assertEquals(2, userService.getAll().size()); + + final UserEntity newEntity = userService.create(new UserEntity("asfzxv", "aasd@asd", "qwtg", null)); + Assertions.assertEquals(3, userService.getAll().size()); + Assertions.assertNotEquals(user.getId(), newEntity.getId()); + } +} diff --git a/3 lab/src/test/resources/application.properties b/3 lab/src/test/resources/application.properties new file mode 100644 index 0000000..d5f355c --- /dev/null +++ b/3 lab/src/test/resources/application.properties @@ -0,0 +1,14 @@ +# Server +spring.main.banner-mode=off + +# Logger settings +# Available levels are: TRACE, DEBUG, INFO, WARN, ERROR, FATAL, OFF +logging.level.com.example.demo=DEBUG + +# JPA Settings +spring.datasource.url=jdbc:h2:mem:testdb +spring.datasource.username=sa +spring.datasource.password=password +spring.datasource.driver-class-name=org.h2.Driver +spring.jpa.hibernate.ddl-auto=create +spring.jpa.open-in-view=false \ No newline at end of file diff --git a/5 lab/.gitignore b/5 lab/.gitignore new file mode 100644 index 0000000..546ecee --- /dev/null +++ b/5 lab/.gitignore @@ -0,0 +1,36 @@ +HELP.md +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +data.*.db \ No newline at end of file diff --git a/5 lab/.vscode/extensions.json b/5 lab/.vscode/extensions.json new file mode 100644 index 0000000..42cf79d --- /dev/null +++ b/5 lab/.vscode/extensions.json @@ -0,0 +1,12 @@ +{ + "recommendations": [ + // fronted + "AndersEAndersen.html-class-suggestions", + "dbaeumer.vscode-eslint", + // backend + "vscjava.vscode-java-pack", + "vmware.vscode-boot-dev-pack", + "vscjava.vscode-gradle", + "redhat.vscode-xml" + ] +} \ No newline at end of file diff --git a/5 lab/.vscode/launch.json b/5 lab/.vscode/launch.json new file mode 100644 index 0000000..85ffc3e --- /dev/null +++ b/5 lab/.vscode/launch.json @@ -0,0 +1,14 @@ +{ + "configurations": [ + { + "type": "java", + "name": "Demo", + "request": "launch", + "cwd": "${workspaceFolder}", + "mainClass": "com.example.demo.DemoApplication", + "projectName": "5 lab", + "args": "--populate", + "envFile": "${workspaceFolder}/.env" + } + ] +} \ No newline at end of file diff --git a/5 lab/.vscode/settings.json b/5 lab/.vscode/settings.json new file mode 100644 index 0000000..95915c9 --- /dev/null +++ b/5 lab/.vscode/settings.json @@ -0,0 +1,24 @@ +{ + "editor.tabSize": 4, + "editor.detectIndentation": false, + "editor.insertSpaces": true, + "editor.formatOnPaste": true, + "editor.formatOnSave": true, + "editor.formatOnType": false, + "java.compile.nullAnalysis.mode": "disabled", + "java.configuration.updateBuildConfiguration": "automatic", + "[java]": { + "editor.pasteAs.enabled": false, + }, + "gradle.nestedProjects": true, + "java.saveActions.organizeImports": true, + "java.dependency.packagePresentation": "hierarchical", + "spring-boot.ls.problem.boot2.JAVA_CONSTRUCTOR_PARAMETER_INJECTION": "WARNING", + "spring.initializr.defaultLanguage": "Java", + "java.format.settings.url": ".vscode/eclipse-formatter.xml", + "java.project.explorer.showNonJavaResources": true, + "java.codeGeneration.hashCodeEquals.useJava7Objects": true, + "cSpell.words": [ + "classappend" + ], +} \ No newline at end of file diff --git a/5 lab/build.gradle b/5 lab/build.gradle new file mode 100644 index 0000000..138fe43 --- /dev/null +++ b/5 lab/build.gradle @@ -0,0 +1,51 @@ +plugins { + id 'java' + id 'org.springframework.boot' version '3.2.4' + id 'io.spring.dependency-management' version '1.1.4' +} + +group = 'com.example' +version = '0.0.1-SNAPSHOT' + +defaultTasks 'bootRun' + +jar { + enabled = false +} + +bootJar { + archiveFileName = String.format('%s-%s.jar', rootProject.name, version) +} + +assert System.properties['java.specification.version'] == '17' || '21' +java { + sourceCompatibility = '17' +} + +repositories { + mavenCentral() +} + +dependencies { + implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springframework.boot:spring-boot-starter-validation' + implementation 'org.modelmapper:modelmapper:3.2.0' + + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + implementation 'com.h2database:h2:2.2.224' + + implementation 'org.springframework.boot:spring-boot-devtools' + implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' + implementation 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect:3.3.0' + runtimeOnly 'org.webjars.npm:bootstrap:5.3.3' + runtimeOnly 'org.webjars.npm:bootstrap-icons:1.11.3' + + implementation 'org.springframework.boot:spring-boot-starter-security' + implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6' + + testImplementation 'org.springframework.boot:spring-boot-starter-test' +} + +tasks.named('test') { + useJUnitPlatform() +} diff --git a/5 lab/gradle/wrapper/gradle-wrapper.jar b/5 lab/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..d64cd4917707c1f8861d8cb53dd15194d4248596 GIT binary patch literal 43462 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 literal 0 HcmV?d00001 diff --git a/5 lab/gradle/wrapper/gradle-wrapper.properties b/5 lab/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..1af9e09 --- /dev/null +++ b/5 lab/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/5 lab/gradlew b/5 lab/gradlew new file mode 100644 index 0000000..1aa94a4 --- /dev/null +++ b/5 lab/gradlew @@ -0,0 +1,249 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/5 lab/gradlew.bat b/5 lab/gradlew.bat new file mode 100644 index 0000000..93e3f59 --- /dev/null +++ b/5 lab/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/5 lab/readme.md b/5 lab/readme.md new file mode 100644 index 0000000..ac4a1d0 --- /dev/null +++ b/5 lab/readme.md @@ -0,0 +1,15 @@ +H2 Console: \ +http://localhost:8080/h2-console + +JDBC URL: jdbc:h2:file:./data \ +User Name: sa \ +Password: password + +Почитать: + +- Spring Boot CRUD Application with Thymeleaf https://www.baeldung.com/spring-boot-crud-thymeleaf +- Thymeleaf Layout Dialect https://ultraq.github.io/thymeleaf-layout-dialect/ +- Tutorial: Using Thymeleaf https://www.thymeleaf.org/doc/tutorials/3.1/usingthymeleaf.html#introducing-thymeleaf +- Working with Cookies in Spring MVC using @CookieValue Annotation https://www.geeksforgeeks.org/working-with-cookies-in-spring-mvc-using-cookievalue-annotation/ +- Session Attributes in Spring MVC https://www.baeldung.com/spring-mvc-session-attributes +- LazyInitializationException – What it is and the best way to fix it https://thorben-janssen.com/lazyinitializationexception/ diff --git a/5 lab/settings.gradle b/5 lab/settings.gradle new file mode 100644 index 0000000..0a383dd --- /dev/null +++ b/5 lab/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'demo' diff --git a/5 lab/src/main/java/com/example/demo/DemoApplication.java b/5 lab/src/main/java/com/example/demo/DemoApplication.java new file mode 100644 index 0000000..5bd7479 --- /dev/null +++ b/5 lab/src/main/java/com/example/demo/DemoApplication.java @@ -0,0 +1,101 @@ +package com.example.demo; + +import java.sql.Time; +import java.util.ArrayList; +import java.util.Objects; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.CommandLineRunner; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +import java.util.List; + +import com.example.demo.comments.model.CommentEntity; +import com.example.demo.comments.service.CommentService; +import com.example.demo.messages.model.MessageEntity; +import com.example.demo.messages.service.MessageService; +import com.example.demo.posts.model.PostEntity; +import com.example.demo.posts.service.PostService; +import com.example.demo.users.model.UserEntity; +import com.example.demo.users.model.UserRole; +import com.example.demo.users.service.UserService; + +@SpringBootApplication +public class DemoApplication implements CommandLineRunner { + private final Logger log = LoggerFactory.getLogger(DemoApplication.class); + + private final UserService userService; + private final PostService postService; + private final CommentService commentService; + private final MessageService messageService; + + public DemoApplication(UserService userService, PostService postService, CommentService commentService, + MessageService messageService) { + this.userService = userService; + this.postService = postService; + this.commentService = commentService; + this.messageService = messageService; + } + + public static void main(String[] args) { + SpringApplication.run(DemoApplication.class, args); + } + + @Override + public void run(String... args) throws Exception { + if (args.length > 0 && Objects.equals("--populate", args[0])) { + final var admin = new UserEntity("admin", "admin", "admin", null); + admin.setRole(UserRole.ADMIN); + userService.create(admin); + + log.info("Create default users values"); + final List blank = new ArrayList(); + final var user1 = userService.create(new UserEntity("sardq", "aasd@asd", "asdsd", null)); + userService.create(new UserEntity("Remy4508", "jbsdk@asd", "asdsd", null)); + userService.create(new UserEntity("Rand", "jbsdk@asd", "asdsd", null)); + final var user2 = userService.create(new UserEntity("Shirotame", "aasdsad@asd", "123", blank)); + final List friends = new ArrayList<>(); + friends.add(user1); + friends.add(user2); + userService.addFriend(user1.getId(), user2); + + final var user3 = userService.create(new UserEntity("Domeztos", "aaxsd@asd", "asfgd", friends)); + final var user4 = userService.create(new UserEntity("Russel", "aacbxcsd@asd", "zxvb", blank)); + userService.addFriend(admin.getId(), user1); + userService.addFriend(admin.getId(), user2); + log.info("Create default posts values"); + final var post1 = postService.create( + new PostEntity(admin, new Time(System.currentTimeMillis()), "test", Long.valueOf(0), + Long.valueOf(0))); + postService.create( + new PostEntity(user3, new Time(System.currentTimeMillis()), "test", Long.valueOf(11), + Long.valueOf(2))); + postService.create( + new PostEntity(user4, new Time(System.currentTimeMillis()), "test", Long.valueOf(40), + Long.valueOf(15))); + + log.info("Create default comments values"); + commentService + .create(new CommentEntity(user1, post1, new Time(System.currentTimeMillis()), "my comment")); + commentService + .create(new CommentEntity(user3, post1, new Time(System.currentTimeMillis()), "my comment2")); + commentService + .create(new CommentEntity(user4, post1, new Time(System.currentTimeMillis()), "my comment3")); + commentService + .create(new CommentEntity(user4, post1, new Time(System.currentTimeMillis()), "my comment3")); + commentService + .create(new CommentEntity(user4, post1, new Time(System.currentTimeMillis()), "my comment3")); + commentService + .create(new CommentEntity(user4, post1, new Time(System.currentTimeMillis()), "my comment3")); + + log.info("Create default messages values"); + messageService.create(new MessageEntity(admin, user2, new Time(System.currentTimeMillis()), "hello")); + messageService + .create(new MessageEntity(admin, user2, new Time(System.currentTimeMillis()), "how are you")); + messageService + .create(new MessageEntity(user2, admin, new Time(System.currentTimeMillis()), "I'm ok")); + } + } +} diff --git a/5 lab/src/main/java/com/example/demo/comments/api/CommentController.java b/5 lab/src/main/java/com/example/demo/comments/api/CommentController.java new file mode 100644 index 0000000..3351105 --- /dev/null +++ b/5 lab/src/main/java/com/example/demo/comments/api/CommentController.java @@ -0,0 +1,138 @@ +package com.example.demo.comments.api; + +import java.util.Map; +import org.modelmapper.ModelMapper; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.servlet.mvc.support.RedirectAttributes; + +import com.example.demo.comments.model.CommentEntity; +import com.example.demo.comments.service.CommentService; +import com.example.demo.core.convert; +import com.example.demo.core.api.PageAttributesMapper; +import com.example.demo.core.configuration.Constants; +import com.example.demo.posts.service.PostService; +import com.example.demo.users.service.UserService; + +import jakarta.validation.Valid; + +@Controller +@RequestMapping(CommentController.URL) +public class CommentController { + public static final String URL = Constants.ADMIN_PREFIX + "/comment"; + private static final String COMMENT_VIEW = "comment"; + private static final String COMMENT_EDIT_VIEW = "comment-edit"; + private static final String COMMENT_ATTRIBUTE = "comment"; + private static final String PAGE_ATTRIBUTE = "page"; + + private final UserService userService; + private final PostService postService; + private final CommentService commentService; + private final ModelMapper modelMapper; + + public CommentController(CommentService commentService, UserService userService, PostService postService, + ModelMapper modelMapper) { + this.userService = userService; + this.commentService = commentService; + this.postService = postService; + this.modelMapper = modelMapper; + } + + private CommentDto toDto(CommentEntity entity) { + var model = modelMapper.map(entity, CommentDto.class); + model.setPersonUsername(entity.getperson().getUsername()); + convert time = new convert(); + model.setTime(time.dateToString(entity.getTime())); + return model; + } + + private CommentEntity toEntity(CommentDto dto) { + final CommentEntity entity = modelMapper.map(dto, CommentEntity.class); + entity.setperson(userService.get(dto.getUserId())); + entity.setPost(postService.get(dto.getPostId())); + return entity; + } + + @GetMapping + public String getAll( + @RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page, + Model model) { + final Map attributes = PageAttributesMapper.toAttributes( + commentService.getAll(0L, page, Constants.DEFUALT_PAGE_SIZE), this::toDto); + model.addAllAttributes(attributes); + model.addAttribute(PAGE_ATTRIBUTE, page); + return COMMENT_VIEW; + } + + @GetMapping("/edit/") + public String create( + @RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page, + Model model) { + model.addAttribute(COMMENT_ATTRIBUTE, new CommentDto()); + model.addAttribute(PAGE_ATTRIBUTE, page); + return COMMENT_EDIT_VIEW; + } + + @PostMapping("/edit/") + public String create( + @ModelAttribute(name = COMMENT_ATTRIBUTE) @Valid CommentDto comment, + BindingResult bindingResult, + Model model) { + if (bindingResult.hasErrors()) { + return COMMENT_EDIT_VIEW; + } + commentService.create(toEntity(comment)); + return Constants.REDIRECT_VIEW + URL; + } + + @GetMapping("/edit/{id}") + public String update( + @PathVariable(name = "id") Long id, + @RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page, + Model model) { + if (id <= 0) { + throw new IllegalArgumentException(); + } + model.addAttribute(COMMENT_ATTRIBUTE, toDto(commentService.get(id))); + model.addAttribute(PAGE_ATTRIBUTE, page); + return COMMENT_EDIT_VIEW; + } + + @PostMapping("/edit/{id}") + public String update( + @PathVariable(name = "id") Long id, + @RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page, + @ModelAttribute(name = COMMENT_ATTRIBUTE) @Valid CommentDto post, + BindingResult bindingResult, + Model model, + RedirectAttributes redirectAttributes) { + if (bindingResult.hasErrors()) { + model.addAttribute(PAGE_ATTRIBUTE, page); + return COMMENT_EDIT_VIEW; + } + if (id <= 0) { + throw new IllegalArgumentException(); + } + redirectAttributes.addAttribute(PAGE_ATTRIBUTE, page); + commentService.update(id, toEntity(post)); + return Constants.REDIRECT_VIEW + URL; + } + + @PostMapping("/delete/{id}") + public String delete( + @PathVariable(name = "id") Long id, + @RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page, + RedirectAttributes redirectAttributes) { + redirectAttributes.addAttribute(PAGE_ATTRIBUTE, page); + commentService.delete(id); + return Constants.REDIRECT_VIEW + URL; + } + +} diff --git a/5 lab/src/main/java/com/example/demo/comments/api/CommentDto.java b/5 lab/src/main/java/com/example/demo/comments/api/CommentDto.java new file mode 100644 index 0000000..17b87a7 --- /dev/null +++ b/5 lab/src/main/java/com/example/demo/comments/api/CommentDto.java @@ -0,0 +1,71 @@ +package com.example.demo.comments.api; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; + +public class CommentDto { + private Long id; + @NotNull + @Min(1) + private Long userId; + private String personUsername; + @NotBlank + private String text; + @NotNull + @Min(1) + private Long postId; + @NotBlank + private String time; + + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getUserId() { + return userId; + } + + public void setUserId(Long userId) { + this.userId = userId; + } + + public String getPersonUsername() { + return personUsername; + } + + public void setPersonUsername(String personUsername) { + this.personUsername = personUsername; + } + + public Long getPostId() { + return postId; + } + + public void setPostId(Long postId) { + this.postId = postId; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + + public String getTime() { + return time; + } + + public void setTime(String time) { + this.time = time; + } +} diff --git a/5 lab/src/main/java/com/example/demo/comments/api/PostCommentDto.java b/5 lab/src/main/java/com/example/demo/comments/api/PostCommentDto.java new file mode 100644 index 0000000..a417919 --- /dev/null +++ b/5 lab/src/main/java/com/example/demo/comments/api/PostCommentDto.java @@ -0,0 +1,64 @@ +package com.example.demo.comments.api; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import jakarta.validation.constraints.NotBlank; + +public class PostCommentDto { + private Long id; + private Long personId; + private String personUsername; + @NotBlank + private String text; + private Long postId = 0L; + private String time = ""; + + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getPersonId() { + return personId; + } + + public void setPersonId(Long personId) { + this.personId = personId; + } + + public String getPersonUsername() { + return personUsername; + } + + public void setPersonUsername(String personUsername) { + this.personUsername = personUsername; + } + + public Long getPostId() { + return postId; + } + + public void setPostId(Long postId) { + this.postId = postId; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + + public String getTime() { + return time; + } + + public void setTime(String time) { + this.time = time; + } +} diff --git a/5 lab/src/main/java/com/example/demo/comments/model/CommentEntity.java b/5 lab/src/main/java/com/example/demo/comments/model/CommentEntity.java new file mode 100644 index 0000000..4eac940 --- /dev/null +++ b/5 lab/src/main/java/com/example/demo/comments/model/CommentEntity.java @@ -0,0 +1,95 @@ +package com.example.demo.comments.model; + +import java.util.Date; +import java.util.Objects; + +import org.hibernate.annotations.OnDelete; +import org.hibernate.annotations.OnDeleteAction; + +import com.example.demo.core.model.BaseEntity; +import com.example.demo.posts.model.PostEntity; +import com.example.demo.users.model.UserEntity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; + +@Entity +@Table(name = "comments") +public class CommentEntity extends BaseEntity { + @ManyToOne + @JoinColumn(name = "personEntity", nullable = false) + @OnDelete(action = OnDeleteAction.CASCADE) + private UserEntity person; + @ManyToOne + @OnDelete(action = OnDeleteAction.CASCADE) + @JoinColumn(name = "postEntity", nullable = false) + private PostEntity post; + @Column(nullable = false) + private Date time; + @Column(length = 100) + private String text; + + public CommentEntity() { + } + + public CommentEntity(UserEntity person, PostEntity post, Date time, String text) { + this.person = person; + this.post = post; + this.time = time; + this.text = text; + } + + public UserEntity getperson() { + return person; + } + + public void setperson(UserEntity person) { + this.person = person; + } + + public PostEntity getPost() { + return post; + } + + public void setPost(PostEntity post) { + this.post = post; + } + + public Date getTime() { + return time; + } + + public void setTime(Date time) { + this.time = time; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + + @Override + public int hashCode() { + return Objects.hash(id, person, post, text, time); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null || getClass() != obj.getClass()) + return false; + final CommentEntity other = (CommentEntity) obj; + return Objects.equals(other.getId(), id) + && Objects.equals(other.getperson(), person) + && Objects.equals(other.getPost(), post) + && Objects.equals(other.getText(), text) + && Objects.equals(other.getTime(), time); + } +} diff --git a/5 lab/src/main/java/com/example/demo/comments/repository/CommentRepository.java b/5 lab/src/main/java/com/example/demo/comments/repository/CommentRepository.java new file mode 100644 index 0000000..166b22c --- /dev/null +++ b/5 lab/src/main/java/com/example/demo/comments/repository/CommentRepository.java @@ -0,0 +1,25 @@ +package com.example.demo.comments.repository; + +import java.util.Optional; +import java.util.List; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.PagingAndSortingRepository; + +import com.example.demo.comments.model.CommentEntity; + +public interface CommentRepository + extends CrudRepository, PagingAndSortingRepository { + + Optional findById(Long Id); + + List findByPostId(long postId); + + Page findAllByPostId(Long postId, Pageable pageable); + + @Query("SELECT p FROM PostEntity as p LEFT JOIN CommentEntity as c ON c.post.id=p.id GROUP BY p") + Page findAllComments(Pageable pageable); +} \ No newline at end of file diff --git a/5 lab/src/main/java/com/example/demo/comments/service/CommentService.java b/5 lab/src/main/java/com/example/demo/comments/service/CommentService.java new file mode 100644 index 0000000..6d4c09c --- /dev/null +++ b/5 lab/src/main/java/com/example/demo/comments/service/CommentService.java @@ -0,0 +1,90 @@ +package com.example.demo.comments.service; + +import java.util.List; +import java.util.stream.StreamSupport; +import java.sql.Time; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.PageRequest; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.example.demo.comments.model.CommentEntity; +import com.example.demo.comments.repository.CommentRepository; +import com.example.demo.core.error.NotFoundException; + +@Service +public class CommentService { + private final CommentRepository repository; + + public CommentService(CommentRepository repository) { + this.repository = repository; + } + + @Transactional(readOnly = true) + public List getAll(int postId) { + if (postId <= 0L) { + return StreamSupport.stream(repository.findAll().spliterator(), false).toList(); + } else { + return repository.findByPostId(postId); + } + + } + + @Transactional(readOnly = true) + public Page getAll(Long postId, int page, int size) { + final Pageable pageRequest = PageRequest.of(page, size); + if (postId <= 0L) { + return repository.findAll(pageRequest); + } else { + return repository.findAllByPostId(postId, pageRequest); + } + } + + @Transactional(readOnly = true) + public CommentEntity getOneByPostId(Long postId) { + var list = repository.findByPostId(postId); + return list.get(0); + } + + @Transactional(readOnly = true) + public Page getAllByParams(int page, int size, Long postId) { + Pageable pageRequest = PageRequest.of(page, size); + if (postId != null) { + pageRequest = PageRequest.of(page, size); + return repository.findAllByPostId(postId, pageRequest); + } + return repository.findAllComments(pageRequest); + } + + @Transactional(readOnly = true) + public CommentEntity get(Long id) { + return repository.findById(id) + .orElseThrow(() -> new NotFoundException(CommentEntity.class, id)); + } + + @Transactional + public CommentEntity create(CommentEntity entity) { + if (entity == null) { + throw new IllegalArgumentException("Entity is null"); + } + entity.setTime(new Time(System.currentTimeMillis())); + return repository.save(entity); + } + + @Transactional + public CommentEntity update(Long id, CommentEntity entity) { + final CommentEntity existsEntity = get(id); + existsEntity.setText(entity.getText()); + repository.save(existsEntity); + return existsEntity; + } + + @Transactional + public CommentEntity delete(Long id) { + final CommentEntity existsEntity = get(id); + repository.delete(existsEntity); + return existsEntity; + } +} diff --git a/5 lab/src/main/java/com/example/demo/core/api/GlobalController.java b/5 lab/src/main/java/com/example/demo/core/api/GlobalController.java new file mode 100644 index 0000000..7794580 --- /dev/null +++ b/5 lab/src/main/java/com/example/demo/core/api/GlobalController.java @@ -0,0 +1,19 @@ +package com.example.demo.core.api; + +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ModelAttribute; + +import jakarta.servlet.http.HttpServletRequest; + +@ControllerAdvice +public class GlobalController { + + public GlobalController() { + } + + @ModelAttribute("servletPath") + String getRequestServletPath(HttpServletRequest request) { + return request.getServletPath(); + } + +} diff --git a/5 lab/src/main/java/com/example/demo/core/api/PageAttributesMapper.java b/5 lab/src/main/java/com/example/demo/core/api/PageAttributesMapper.java new file mode 100644 index 0000000..74ee38d --- /dev/null +++ b/5 lab/src/main/java/com/example/demo/core/api/PageAttributesMapper.java @@ -0,0 +1,18 @@ +package com.example.demo.core.api; + +import java.util.Map; +import java.util.function.Function; + +import org.springframework.data.domain.Page; + +public class PageAttributesMapper { + private PageAttributesMapper() { + } + + public static Map toAttributes(Page page, Function mapper) { + return Map.of( + "items", page.getContent().stream().map(mapper::apply).toList(), + "currentPage", page.getNumber(), + "totalPages", page.getTotalPages()); + } +} diff --git a/5 lab/src/main/java/com/example/demo/core/configuration/Constants.java b/5 lab/src/main/java/com/example/demo/core/configuration/Constants.java new file mode 100644 index 0000000..0bf6e30 --- /dev/null +++ b/5 lab/src/main/java/com/example/demo/core/configuration/Constants.java @@ -0,0 +1,19 @@ +package com.example.demo.core.configuration; + +public class Constants { + public static final String SEQUENCE_NAME = "hibernate_sequence"; + + public static final int DEFUALT_PAGE_SIZE = 5; + + public static final String REDIRECT_VIEW = "redirect:"; + + public static final String ADMIN_PREFIX = "/admin"; + + public static final String LOGIN_URL = "/login"; + public static final String LOGOUT_URL = "/logout"; + + public static final String DEFAULT_PASSWORD = "123456"; + + private Constants() { + } +} diff --git a/5 lab/src/main/java/com/example/demo/core/configuration/MapperConfiguration.java b/5 lab/src/main/java/com/example/demo/core/configuration/MapperConfiguration.java new file mode 100644 index 0000000..44defae --- /dev/null +++ b/5 lab/src/main/java/com/example/demo/core/configuration/MapperConfiguration.java @@ -0,0 +1,23 @@ +package com.example.demo.core.configuration; + +import org.modelmapper.ModelMapper; +import org.modelmapper.PropertyMap; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import com.example.demo.core.model.BaseEntity; + +@Configuration +public class MapperConfiguration { + @Bean + ModelMapper modelMapper() { + final ModelMapper mapper = new ModelMapper(); + mapper.addMappings(new PropertyMap() { + @Override + protected void configure() { + skip(destination.getId()); + } + }); + return mapper; + } +} diff --git a/5 lab/src/main/java/com/example/demo/core/configuration/WebConfiguration.java b/5 lab/src/main/java/com/example/demo/core/configuration/WebConfiguration.java new file mode 100644 index 0000000..6316aa8 --- /dev/null +++ b/5 lab/src/main/java/com/example/demo/core/configuration/WebConfiguration.java @@ -0,0 +1,13 @@ +package com.example.demo.core.configuration; + +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +public class WebConfiguration implements WebMvcConfigurer { + @Override + public void addViewControllers(ViewControllerRegistry registry) { + registry.addViewController("/login").setViewName("login"); + } +} diff --git a/5 lab/src/main/java/com/example/demo/core/convert.java b/5 lab/src/main/java/com/example/demo/core/convert.java new file mode 100644 index 0000000..a8035a6 --- /dev/null +++ b/5 lab/src/main/java/com/example/demo/core/convert.java @@ -0,0 +1,22 @@ +package com.example.demo.core; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; + +public class convert { + public Date stringToDate(String string) throws Exception { + DateFormat format = new SimpleDateFormat("HH:mm:ss"); + return (Date) format.parse(string); + } + + public String dateToString(Date date) { + DateFormat dateFormat = new SimpleDateFormat("dd.MM.YYYY hh:mm:ss"); + return dateFormat.format(date); + } + + public String dateToFormatDTOString(Date date) { + DateFormat dateFormat = new SimpleDateFormat("YYYY-MM-dd"); + return dateFormat.format(date); + } +} diff --git a/5 lab/src/main/java/com/example/demo/core/error/AdviceController.java b/5 lab/src/main/java/com/example/demo/core/error/AdviceController.java new file mode 100644 index 0000000..65f0c3e --- /dev/null +++ b/5 lab/src/main/java/com/example/demo/core/error/AdviceController.java @@ -0,0 +1,53 @@ +package com.example.demo.core.error; + +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.servlet.ModelAndView; + +import jakarta.servlet.http.HttpServletRequest; + +@ControllerAdvice +public class AdviceController { + private final Logger log = LoggerFactory.getLogger(AdviceController.class); + + private static Throwable getRootCause(Throwable throwable) { + Throwable rootCause = throwable; + while (rootCause.getCause() != null && rootCause.getCause() != rootCause) { + rootCause = rootCause.getCause(); + } + return rootCause; + } + + private static Map getAttributes(HttpServletRequest request, Throwable throwable) { + final Throwable rootCause = getRootCause(throwable); + final StackTraceElement firstError = rootCause.getStackTrace()[0]; + return Map.of( + "message", rootCause.getMessage(), + "url", request.getRequestURL(), + "exception", rootCause.getClass().getName(), + "file", firstError.getFileName(), + "method", firstError.getMethodName(), + "line", firstError.getLineNumber()); + } + + @ExceptionHandler(value = Exception.class) + public ModelAndView defaultErrorHandler(HttpServletRequest request, Throwable throwable) throws Throwable { + if (AnnotationUtils.findAnnotation(throwable.getClass(), + ResponseStatus.class) != null) { + throw throwable; + } + + log.error("{}", throwable.getMessage()); + throwable.printStackTrace(); + final ModelAndView model = new ModelAndView(); + model.addAllObjects(getAttributes(request, throwable)); + model.setViewName("error"); + return model; + } +} diff --git a/5 lab/src/main/java/com/example/demo/core/error/NotFoundException.java b/5 lab/src/main/java/com/example/demo/core/error/NotFoundException.java new file mode 100644 index 0000000..a61d118 --- /dev/null +++ b/5 lab/src/main/java/com/example/demo/core/error/NotFoundException.java @@ -0,0 +1,7 @@ +package com.example.demo.core.error; + +public class NotFoundException extends RuntimeException { + public NotFoundException(Class clazz, Long id) { + super(String.format("%s with id [%s] is not found or not exists", clazz.getSimpleName(), id)); + } +} diff --git a/5 lab/src/main/java/com/example/demo/core/model/BaseEntity.java b/5 lab/src/main/java/com/example/demo/core/model/BaseEntity.java new file mode 100644 index 0000000..eba74ad --- /dev/null +++ b/5 lab/src/main/java/com/example/demo/core/model/BaseEntity.java @@ -0,0 +1,28 @@ +package com.example.demo.core.model; + +import com.example.demo.core.configuration.Constants; + +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.MappedSuperclass; +import jakarta.persistence.SequenceGenerator; + +@MappedSuperclass +public abstract class BaseEntity { + @Id + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = Constants.SEQUENCE_NAME) + @SequenceGenerator(name = Constants.SEQUENCE_NAME, sequenceName = Constants.SEQUENCE_NAME, allocationSize = 1) + protected Long id; + + protected BaseEntity() { + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } +} diff --git a/5 lab/src/main/java/com/example/demo/core/security/SecurityConfiguration.java b/5 lab/src/main/java/com/example/demo/core/security/SecurityConfiguration.java new file mode 100644 index 0000000..6b375a3 --- /dev/null +++ b/5 lab/src/main/java/com/example/demo/core/security/SecurityConfiguration.java @@ -0,0 +1,63 @@ +package com.example.demo.core.security; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.dao.DaoAuthenticationProvider; +import org.springframework.security.config.Customizer; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; +import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer.FrameOptionsConfig; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.SecurityFilterChain; + +import com.example.demo.core.configuration.Constants; +import com.example.demo.users.api.UserSignupController; +import com.example.demo.users.model.UserRole; + +@Configuration +@EnableWebSecurity +public class SecurityConfiguration { + @Bean + SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception { + httpSecurity.headers(headers -> headers.frameOptions(FrameOptionsConfig::sameOrigin)); + httpSecurity.csrf(AbstractHttpConfigurer::disable); + httpSecurity.cors(Customizer.withDefaults()); + + httpSecurity.authorizeHttpRequests(requests -> requests + .requestMatchers("/css/**", "/webjars/**", "/*.svg") + .permitAll()); + + httpSecurity.authorizeHttpRequests(requests -> requests + .requestMatchers(Constants.ADMIN_PREFIX + "/**").hasRole(UserRole.ADMIN.name()) + .requestMatchers("/h2-console/**").hasRole(UserRole.ADMIN.name()) + .requestMatchers(UserSignupController.URL).anonymous() + .requestMatchers(Constants.LOGIN_URL).anonymous() + .anyRequest().authenticated()); + + httpSecurity.formLogin(formLogin -> formLogin + .loginPage(Constants.LOGIN_URL)); + + httpSecurity.rememberMe(rememberMe -> rememberMe.key("uniqueAndSecret")); + + httpSecurity.logout(logout -> logout + .deleteCookies("JSESSIONID")); + + return httpSecurity.build(); + } + + @Bean + DaoAuthenticationProvider authenticationProvider(UserDetailsService userDetailsService) { + final DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider(); + authProvider.setUserDetailsService(userDetailsService); + authProvider.setPasswordEncoder(passwordEncoder()); + return authProvider; + } + + @Bean + PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } +} diff --git a/5 lab/src/main/java/com/example/demo/core/security/UserPrincipal.java b/5 lab/src/main/java/com/example/demo/core/security/UserPrincipal.java new file mode 100644 index 0000000..7e9dc5b --- /dev/null +++ b/5 lab/src/main/java/com/example/demo/core/security/UserPrincipal.java @@ -0,0 +1,64 @@ +package com.example.demo.core.security; + +import java.util.Collection; +import java.util.Set; + +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; + +import com.example.demo.users.model.UserEntity; + +public class UserPrincipal implements UserDetails { + private final long id; + private final String username; + private final String password; + private final Set roles; + private final boolean active; + + public UserPrincipal(UserEntity user) { + this.id = user.getId(); + this.username = user.getUsername(); + this.password = user.getPassword(); + this.roles = Set.of(user.getRole()); + this.active = true; + } + + public Long getId() { + return id; + } + + @Override + public String getUsername() { + return username; + } + + @Override + public String getPassword() { + return password; + } + + @Override + public Collection getAuthorities() { + return roles; + } + + @Override + public boolean isEnabled() { + return active; + } + + @Override + public boolean isAccountNonExpired() { + return isEnabled(); + } + + @Override + public boolean isAccountNonLocked() { + return isEnabled(); + } + + @Override + public boolean isCredentialsNonExpired() { + return isEnabled(); + } +} diff --git a/5 lab/src/main/java/com/example/demo/messages/api/DialogController.java b/5 lab/src/main/java/com/example/demo/messages/api/DialogController.java new file mode 100644 index 0000000..cf0e82b --- /dev/null +++ b/5 lab/src/main/java/com/example/demo/messages/api/DialogController.java @@ -0,0 +1,153 @@ +package com.example.demo.messages.api; + +import java.util.Map; +import java.sql.Time; + +import org.modelmapper.ModelMapper; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.servlet.mvc.support.RedirectAttributes; + +import com.example.demo.core.convert; +import com.example.demo.core.api.PageAttributesMapper; +import com.example.demo.core.configuration.Constants; +import com.example.demo.core.security.UserPrincipal; +import com.example.demo.messages.model.MessageEntity; +import com.example.demo.messages.service.MessageService; +import com.example.demo.users.api.UserDto; +import com.example.demo.users.model.UserEntity; +import com.example.demo.users.service.UserService; + +import jakarta.validation.Valid; + +@Controller +@RequestMapping(DialogController.URL) +public class DialogController { + + public static final String URL = "/dialogs"; + private static final String DIALOGS_VIEW = "dialogs"; + private static final String MESSAGE_VIEW = "dialog"; + private static final String PAGE_ATTRIBUTE = "page"; + private static final String MESSAGE_ATTRIBUTE = "message"; + + private final MessageService messageService; + private final UserService userService; + private final ModelMapper modelMapper; + + public DialogController(MessageService messageService, ModelMapper modelMapper, UserService userService) { + this.messageService = messageService; + this.modelMapper = modelMapper; + this.userService = userService; + } + + private MessageDto toDto(MessageEntity entity) { + var model = modelMapper.map(entity, MessageDto.class); + convert time = new convert(); + model.setRecieverUsername(entity.getFriend().getUsername()); + model.setSenderUsername(entity.getPerson().getUsername()); + model.setTime(time.dateToString(entity.getTime())); + return model; + } + + private UserDto toUserDto(UserEntity entity) { + var model = modelMapper.map(entity, UserDto.class); + if (entity.getFriends() != null) { + for (UserEntity user : entity.getFriends()) { + model.addFriendId(user.getId()); + } + } + return model; + + } + + private MessageEntity toEntity(DialogDto dto) { + final MessageEntity entity = modelMapper.map(dto, MessageEntity.class); + entity.setFriend(userService.get(dto.getFriendId())); + entity.setPerson(userService.get(dto.getPersonId())); + return entity; + + } + + @GetMapping + public String getAll( + @RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page, + Model model, + @AuthenticationPrincipal UserPrincipal userEntity) { + var user = userService.get(userEntity.getId()); + model.addAttribute("principal", toUserDto(user)); + final Map attributes = PageAttributesMapper.toAttributes( + userService.getByFriends(user.getFriends(), page, Constants.DEFUALT_PAGE_SIZE), this::toUserDto); + model.addAllAttributes(attributes); + model.addAttribute(PAGE_ATTRIBUTE, page); + return DIALOGS_VIEW; + } + + @GetMapping("/{id}") + public String getDialogMessages( + @PathVariable(name = "id") Long id, + @RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page, + @AuthenticationPrincipal UserPrincipal principal, + Model model) { + if (id <= 0) { + throw new IllegalArgumentException(); + } + var user = userService.get(principal.getId()); + var friend = userService.get(id); + Map attributes = PageAttributesMapper + .toAttributes(messageService.getAll(principal.getId(), id, page, 5), + this::toDto); + model.addAllAttributes(attributes); + model.addAttribute("principal", toUserDto(user)); + model.addAttribute("user", toUserDto(friend)); + var newMessage = new MessageDto(); + newMessage.setPersonId(user.getId()); + newMessage.setFriendId(id); + newMessage.setText("hello"); + model.addAttribute("chatmsg", newMessage); + return MESSAGE_VIEW; + } + + @PostMapping("{id}/edit") + public String createDialogMessage( + @PathVariable(name = "id") Long id, + @RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page, + @ModelAttribute(name = MESSAGE_ATTRIBUTE) @Valid DialogDto message, + BindingResult bindingResult, + @AuthenticationPrincipal UserPrincipal principal, + Model model, + RedirectAttributes redirectAttributes) { + message.setPersonId(principal.getId()); + message.setSenderUsername(principal.getUsername()); + message.setFriendId(id); + if (message.getText().length() < 4) { + bindingResult.rejectValue("text", "profile:texts", "Неправильный текст."); + model.addAttribute(MESSAGE_VIEW, message); + return MESSAGE_VIEW; + } + var friend = userService.get(id); + message.setRecieverUsername(friend.getUsername()); + convert conv = new convert(); + message.setTime(conv.dateToFormatDTOString(new Time(System.currentTimeMillis()))); + redirectAttributes.addAttribute(PAGE_ATTRIBUTE, page); + messageService.create(toEntity(message)); + return Constants.REDIRECT_VIEW + "/dialogs/" + id; + } + + @PostMapping("/delete/{id}") + public String delete( + @PathVariable(name = "id") Long id, + @RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page, + RedirectAttributes redirectAttributes) { + redirectAttributes.addAttribute(PAGE_ATTRIBUTE, page); + messageService.delete(id); + return Constants.REDIRECT_VIEW + URL; + } +} diff --git a/5 lab/src/main/java/com/example/demo/messages/api/DialogDto.java b/5 lab/src/main/java/com/example/demo/messages/api/DialogDto.java new file mode 100644 index 0000000..78b9d24 --- /dev/null +++ b/5 lab/src/main/java/com/example/demo/messages/api/DialogDto.java @@ -0,0 +1,70 @@ +package com.example.demo.messages.api; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class DialogDto { + private Long id; + private Long friendId; + private Long personId; + private String text; + private String time; + private String senderUsername; + private String recieverUsername; + + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getPersonId() { + return personId; + } + + public void setPersonId(Long personId) { + this.personId = personId; + } + + public String getSenderUsername() { + return senderUsername; + } + + public void setSenderUsername(String senderUsername) { + this.senderUsername = senderUsername; + } + + public String getRecieverUsername() { + return recieverUsername; + } + + public void setRecieverUsername(String recieverUsername) { + this.recieverUsername = recieverUsername; + } + + public Long getFriendId() { + return friendId; + } + + public void setFriendId(Long friendId) { + this.friendId = friendId; + } + + public String getTime() { + return time; + } + + public void setTime(String time) { + this.time = time; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } +} diff --git a/5 lab/src/main/java/com/example/demo/messages/api/MessageController.java b/5 lab/src/main/java/com/example/demo/messages/api/MessageController.java new file mode 100644 index 0000000..e2f4521 --- /dev/null +++ b/5 lab/src/main/java/com/example/demo/messages/api/MessageController.java @@ -0,0 +1,77 @@ +package com.example.demo.messages.api; + +import java.util.Map; + +import org.modelmapper.ModelMapper; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.servlet.mvc.support.RedirectAttributes; + +import com.example.demo.core.convert; +import com.example.demo.core.api.PageAttributesMapper; +import com.example.demo.core.configuration.Constants; +import com.example.demo.core.security.UserPrincipal; +import com.example.demo.messages.model.MessageEntity; +import com.example.demo.messages.service.MessageService; + +@Controller +@RequestMapping(MessageController.URL) +public class MessageController { + + public static final String URL = Constants.ADMIN_PREFIX + "/message"; + private static final String MESSAGE_VIEW = "message"; + private static final String PAGE_ATTRIBUTE = "page"; + + private final MessageService messageService; + private final ModelMapper modelMapper; + + public MessageController(MessageService messageService, ModelMapper modelMapper) { + this.messageService = messageService; + this.modelMapper = modelMapper; + } + + private MessageDto toDto(MessageEntity entity) { + var model = modelMapper.map(entity, MessageDto.class); + convert time = new convert(); + model.setRecieverUsername(entity.getFriend().getUsername()); + model.setSenderUsername(entity.getPerson().getUsername()); + model.setTime(time.dateToString(entity.getTime())); + return model; + } + + // private MessageEntity toEntity(MessageDto dto) { + // final MessageEntity entity = modelMapper.map(dto, MessageEntity.class); + // entity.setFriend(userService.get(dto.getFriendId())); + // entity.setPerson(userService.get(dto.getPersonId())); + // return entity; + + // } + + @GetMapping + public String getAll( + @RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page, + Model model, + @AuthenticationPrincipal UserPrincipal userEntity) { + final Map attributes = PageAttributesMapper.toAttributes( + messageService.getAll(0L, 0L, page, Constants.DEFUALT_PAGE_SIZE), this::toDto); + model.addAllAttributes(attributes); + model.addAttribute(PAGE_ATTRIBUTE, page); + return MESSAGE_VIEW; + } + + @PostMapping("/delete/{id}") + public String delete( + @PathVariable(name = "id") Long id, + @RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page, + RedirectAttributes redirectAttributes) { + redirectAttributes.addAttribute(PAGE_ATTRIBUTE, page); + messageService.delete(id); + return Constants.REDIRECT_VIEW + URL; + } +} diff --git a/5 lab/src/main/java/com/example/demo/messages/api/MessageDto.java b/5 lab/src/main/java/com/example/demo/messages/api/MessageDto.java new file mode 100644 index 0000000..b2584e1 --- /dev/null +++ b/5 lab/src/main/java/com/example/demo/messages/api/MessageDto.java @@ -0,0 +1,80 @@ +package com.example.demo.messages.api; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; + +public class MessageDto { + private Long id; + @NotNull + @Min(1) + private Long friendId; + @NotNull + @Min(1) + private Long personId; + @NotBlank + private String text; + @NotBlank + private String time; + private String senderUsername; + private String recieverUsername; + + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getPersonId() { + return personId; + } + + public void setPersonId(Long personId) { + this.personId = personId; + } + + public String getSenderUsername() { + return senderUsername; + } + + public void setSenderUsername(String senderUsername) { + this.senderUsername = senderUsername; + } + + public String getRecieverUsername() { + return recieverUsername; + } + + public void setRecieverUsername(String recieverUsername) { + this.recieverUsername = recieverUsername; + } + + public Long getFriendId() { + return friendId; + } + + public void setFriendId(Long friendId) { + this.friendId = friendId; + } + + public String getTime() { + return time; + } + + public void setTime(String time) { + this.time = time; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } +} diff --git a/5 lab/src/main/java/com/example/demo/messages/model/MessageEntity.java b/5 lab/src/main/java/com/example/demo/messages/model/MessageEntity.java new file mode 100644 index 0000000..5fe5706 --- /dev/null +++ b/5 lab/src/main/java/com/example/demo/messages/model/MessageEntity.java @@ -0,0 +1,96 @@ +package com.example.demo.messages.model; + +import java.util.Date; +import java.util.Objects; + +import org.hibernate.annotations.OnDelete; +import org.hibernate.annotations.OnDeleteAction; + +import com.example.demo.core.model.BaseEntity; +import com.example.demo.users.model.UserEntity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; + +@Entity +@Table(name = "messages") +public class MessageEntity extends BaseEntity { + + @ManyToOne + @OnDelete(action = OnDeleteAction.CASCADE) + @JoinColumn(name = "userEntity", nullable = false) + private UserEntity person; + @ManyToOne + @OnDelete(action = OnDeleteAction.CASCADE) + @JoinColumn(name = "dialogFriend", nullable = false) + private UserEntity friend; + @Column(nullable = false) + private Date time; + @Column(length = 100) + private String text; + + public MessageEntity() { + } + + public MessageEntity(UserEntity person, UserEntity friend, Date time, String text) { + this.person = person; + this.friend = friend; + this.text = text; + this.time = time; + } + + public UserEntity getFriend() { + return friend; + } + + public void setFriend(UserEntity friend) { + this.friend = friend; + } + + public UserEntity getPerson() { + return person; + } + + public void setPerson(UserEntity person) { + this.person = person; + } + + public Date getTime() { + return time; + } + + public void setTime(Date time) { + this.time = time; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + + @Override + public int hashCode() { + return Objects.hash(id, friend, person, time, text); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null || getClass() != obj.getClass()) + return false; + final MessageEntity other = (MessageEntity) obj; + return Objects.equals(other.getId(), id) + && Objects.equals(other.getPerson(), person) + && Objects.equals(other.getFriend(), friend) + && Objects.equals(other.getText(), text) + && Objects.equals(other.getTime(), time); + } + +} diff --git a/5 lab/src/main/java/com/example/demo/messages/repository/MessageRepository.java b/5 lab/src/main/java/com/example/demo/messages/repository/MessageRepository.java new file mode 100644 index 0000000..8caf5bd --- /dev/null +++ b/5 lab/src/main/java/com/example/demo/messages/repository/MessageRepository.java @@ -0,0 +1,29 @@ +package com.example.demo.messages.repository; + +import java.util.List; +import java.util.Optional; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.PagingAndSortingRepository; +import org.springframework.data.repository.query.Param; + +import com.example.demo.messages.model.MessageEntity; + +public interface MessageRepository + extends CrudRepository, PagingAndSortingRepository { + Page findAll(Pageable pageable); + + List findByPersonId(Long personId); + + @Query("SELECT m FROM MessageEntity m WHERE (m.person.id=:personId AND m.friend.id=:friendId) OR (m.friend.id=:personId AND m.person.id=:friendId)") + Page findByPersonIdOrFriendId(@Param("personId") Long personId, + @Param("friendId") Long friendId, Pageable pageable); + + Page findByPersonId(Long personId, Pageable pageable); + + Optional findById(Long Id); + +} \ No newline at end of file diff --git a/5 lab/src/main/java/com/example/demo/messages/service/MessageService.java b/5 lab/src/main/java/com/example/demo/messages/service/MessageService.java new file mode 100644 index 0000000..983fcf0 --- /dev/null +++ b/5 lab/src/main/java/com/example/demo/messages/service/MessageService.java @@ -0,0 +1,65 @@ +package com.example.demo.messages.service; + +import java.sql.Time; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; + +import com.example.demo.core.error.NotFoundException; +import com.example.demo.messages.model.MessageEntity; +import com.example.demo.messages.repository.MessageRepository; + +@Service +public class MessageService { + private final MessageRepository repository; + + public MessageService(MessageRepository repository) { + this.repository = repository; + } + + @Transactional(readOnly = true) + public Page getAll(int page, int size) { + return repository.findAll(PageRequest.of(page, size)); + } + + @Transactional(readOnly = true) + public Page getAll(long personId, long friendId, int page, int size) { + final Pageable pageRequest = PageRequest.of(page, size); + if (personId <= 0L || friendId <= 0L) { + return repository.findAll(pageRequest); + } + return repository.findByPersonIdOrFriendId(personId, friendId, pageRequest); + } + + @Transactional + public MessageEntity get(Long id) { + return repository.findById(id) + .orElseThrow(() -> new NotFoundException(MessageEntity.class, id)); + } + + @Transactional + public MessageEntity create(MessageEntity entity) { + if (entity == null) { + throw new IllegalArgumentException("Entity is null"); + } + entity.setTime(new Time(System.currentTimeMillis())); + return repository.save(entity); + } + + @Transactional + public MessageEntity update(Long id, MessageEntity entity) { + final MessageEntity existsEntity = get(id); + existsEntity.setText(entity.getText()); + repository.save(existsEntity); + return existsEntity; + } + + @Transactional + public MessageEntity delete(Long id) { + final MessageEntity existsEntity = get(id); + repository.delete(existsEntity); + return existsEntity; + } +} diff --git a/5 lab/src/main/java/com/example/demo/posts/api/HomeController.java b/5 lab/src/main/java/com/example/demo/posts/api/HomeController.java new file mode 100644 index 0000000..14e8f8b --- /dev/null +++ b/5 lab/src/main/java/com/example/demo/posts/api/HomeController.java @@ -0,0 +1,305 @@ +package com.example.demo.posts.api; + +import java.util.Map; + +import org.modelmapper.ModelMapper; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.servlet.mvc.support.RedirectAttributes; + +import java.sql.Time; + +import com.example.demo.comments.api.CommentDto; +import com.example.demo.comments.api.PostCommentDto; +import com.example.demo.comments.model.CommentEntity; +import com.example.demo.comments.service.CommentService; +import com.example.demo.core.convert; +import com.example.demo.core.api.PageAttributesMapper; +import com.example.demo.core.configuration.Constants; +import com.example.demo.core.security.UserPrincipal; +import com.example.demo.posts.model.PostEntity; +import com.example.demo.posts.service.PostService; +import com.example.demo.users.service.UserService; + +import jakarta.validation.Valid; + +@Controller +public class HomeController { + private static final String HOME_VIEW = "/"; + private static final String PAGE_ATTRIBUTE = "page"; + private static final String COMMENT_ATTRIBUTE = "comment"; + private static final String POST_EDIT_VIEW = "post-edit"; + private static final String COMMENT_EDIT_VIEW = "comment-edit"; + + private static final String POST_ATTRIBUTE = "post"; + private static final String COMMENT_VIEW = "comments"; + + private final UserService userService; + private final CommentService commentService; + + private final PostService postService; + private final ModelMapper modelMapper; + + public HomeController(PostService postService, UserService userService, ModelMapper modelMapper, + CommentService commentService) { + this.commentService = commentService; + this.postService = postService; + this.userService = userService; + this.modelMapper = modelMapper; + } + + private HomePostDto toDto(PostEntity entity) { + var model = modelMapper.map(entity, HomePostDto.class); + model.setPersonUsername(entity.getPerson().getUsername()); + convert time = new convert(); + model.setTime(time.dateToString(entity.getTime())); + return model; + } + + private PostCommentDto toCommentDto(CommentEntity entity) { + var model = modelMapper.map(entity, PostCommentDto.class); + model.setPersonUsername(entity.getperson().getUsername()); + convert time = new convert(); + model.setTime(time.dateToString(entity.getTime())); + return model; + } + + private PostEntity toEntity(HomePostDto dto) { + final PostEntity entity = modelMapper.map(dto, PostEntity.class); + entity.setPerson(userService.get(dto.getPersonId())); + return entity; + + } + + private CommentEntity toCommentEntity(PostCommentDto dto) { + final CommentEntity entity = modelMapper.map(dto, CommentEntity.class); + entity.setperson(userService.get(dto.getPersonId())); + entity.setPost(postService.get(dto.getPostId())); + return entity; + } + + @GetMapping + public String getAll( + @RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page, + @AuthenticationPrincipal UserPrincipal principal, + Model model) { + final Map attributes = PageAttributesMapper.toAttributes( + postService.getAll(page, Constants.DEFUALT_PAGE_SIZE), this::toDto); + model.addAttribute("user", principal); + model.addAllAttributes(attributes); + model.addAttribute(PAGE_ATTRIBUTE, page); + return "home"; + } + + @GetMapping("/edit/") + public String createPost( + @RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page, + Model model) { + model.addAttribute(POST_ATTRIBUTE, new HomePostDto()); + model.addAttribute(PAGE_ATTRIBUTE, page); + return POST_EDIT_VIEW; + } + + @PostMapping("/edit/") + public String createPost( + @RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page, + @ModelAttribute(name = POST_ATTRIBUTE) @Valid HomePostDto post, + @AuthenticationPrincipal UserPrincipal principal, + BindingResult bindingResult, + Model model, + RedirectAttributes redirectAttributes) { + if (post.getText().length() < 4) { + bindingResult.rejectValue("text", "posts:texts", "Неправильный текс."); + model.addAttribute(POST_ATTRIBUTE, post); + return POST_EDIT_VIEW; + } + post.setPersonId(principal.getId()); + post.setPersonUsername(principal.getUsername()); + post.setLiked(0L); + post.setViewed(0L); + convert conv = new convert(); + post.setTime(conv.dateToFormatDTOString(new Time(System.currentTimeMillis()))); + redirectAttributes.addAttribute(PAGE_ATTRIBUTE, page); + postService.create(toEntity(post)); + return Constants.REDIRECT_VIEW + HOME_VIEW; + } + + @PostMapping("/addLike/{id}") + public String addLike(@PathVariable(name = "id") Long id) { + toDto(postService.addLike(id)); + return Constants.REDIRECT_VIEW + HOME_VIEW; + } + + @PostMapping("/remLike/{id}") + public String removeLike(@PathVariable(name = "id") Long id) { + toDto(postService.removeLiked(id)); + return Constants.REDIRECT_VIEW + HOME_VIEW; + } + + @PostMapping("/view/{id}") + public HomePostDto addView(@PathVariable(name = "id") Long id) { + return toDto(postService.addView(id)); + } + + @GetMapping("/edit/{id}") + public String updatePost( + @PathVariable(name = "id") Long id, + @RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page, + @AuthenticationPrincipal UserPrincipal principal, + Model model) { + if (id <= 0) { + throw new IllegalArgumentException(); + } + if (principal.getId() != postService.get(id).getPerson().getId()) { + return Constants.REDIRECT_VIEW + HOME_VIEW; + } + model.addAttribute(POST_ATTRIBUTE, toDto(postService.get(id))); + model.addAttribute(PAGE_ATTRIBUTE, page); + return POST_EDIT_VIEW; + } + + @PostMapping("/edit/{id}") + public String updatePost( + @PathVariable(name = "id") Long id, + @RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page, + @ModelAttribute(name = POST_ATTRIBUTE) @Valid HomePostDto post, + BindingResult bindingResult, + @AuthenticationPrincipal UserPrincipal principal, + Model model, + RedirectAttributes redirectAttributes) { + if (bindingResult.hasErrors()) { + model.addAttribute(PAGE_ATTRIBUTE, page); + return POST_EDIT_VIEW; + } + convert conv = new convert(); + post.setTime(conv.dateToFormatDTOString(new Time(System.currentTimeMillis()))); + if (id <= 0) { + throw new IllegalArgumentException(); + } + post.setPersonId(principal.getId()); + redirectAttributes.addAttribute(PAGE_ATTRIBUTE, page); + postService.update(id, toEntity(post)); + return Constants.REDIRECT_VIEW + HOME_VIEW; + } + + @PostMapping("/delete/{id}") + public String deletePost( + @PathVariable(name = "id") Long id, + @RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page, + @AuthenticationPrincipal UserPrincipal principal, + RedirectAttributes redirectAttributes) { + redirectAttributes.addAttribute(PAGE_ATTRIBUTE, page); + if (postService.get(id).getPerson().getId() == principal.getId()) + postService.delete(id); + return Constants.REDIRECT_VIEW + HOME_VIEW; + } + + @GetMapping("/{id}/comments") + public String getPostComments( + @PathVariable(name = "id") Long id, + @RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page, + @AuthenticationPrincipal UserPrincipal principal, + Model model) { + if (id <= 0) { + throw new IllegalArgumentException(); + } + Map attributes = PageAttributesMapper + .toAttributes(commentService.getAll(id, page, 5), + this::toCommentDto); + model.addAllAttributes(attributes); + model.addAttribute("post", toDto(postService.get(id))); + var newComment = new CommentDto(); + newComment.setPostId(id); + model.addAttribute("comment", newComment); + model.addAttribute("user", principal); + return COMMENT_VIEW; + } + + @PostMapping("{id}/comments/edit") + public String createPostComment( + @PathVariable(name = "id") Long id, + @RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page, + @ModelAttribute(name = COMMENT_ATTRIBUTE) @Valid PostCommentDto comment, + @AuthenticationPrincipal UserPrincipal principal, + BindingResult bindingResult, + Model model, + RedirectAttributes redirectAttributes) { + if (comment.getText().length() < 4) { + bindingResult.rejectValue("text", "comments:texts", "Введите больше символов."); + model.addAttribute(COMMENT_ATTRIBUTE, comment); + return COMMENT_EDIT_VIEW; + } + comment.setPersonId(principal.getId()); + comment.setPersonUsername(principal.getUsername()); + comment.setPostId(id); + convert conv = new convert(); + comment.setTime(conv.dateToFormatDTOString(new Time(System.currentTimeMillis()))); + redirectAttributes.addAttribute(PAGE_ATTRIBUTE, page); + commentService.create(toCommentEntity(comment)); + return Constants.REDIRECT_VIEW + "/" + id + "/" + COMMENT_VIEW; + } + + @GetMapping("{postId}/comments/edit/{id}") + public String updatePostComment( + @PathVariable(name = "postId") Long postId, + @PathVariable(name = "id") Long id, + @RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page, + @AuthenticationPrincipal UserPrincipal principal, + Model model) { + if (id <= 0 && postId <= 0) { + throw new IllegalArgumentException(); + } + if (principal.getId() != commentService.get(id).getperson().getId()) { + return Constants.REDIRECT_VIEW + "/" + postId + "/" + COMMENT_VIEW; + } + model.addAttribute(COMMENT_ATTRIBUTE, toCommentDto(commentService.get(id))); + model.addAttribute(PAGE_ATTRIBUTE, page); + return COMMENT_EDIT_VIEW; + } + + @PostMapping("{postId}/comments/edit/{id}") + public String updatePostComment( + @PathVariable(name = "id") Long id, + @PathVariable(name = "postId") Long postId, + @RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page, + @ModelAttribute(name = COMMENT_ATTRIBUTE) @Valid PostCommentDto comment, + BindingResult bindingResult, + @AuthenticationPrincipal UserPrincipal principal, + Model model, + RedirectAttributes redirectAttributes) { + if (bindingResult.hasErrors()) { + model.addAttribute(PAGE_ATTRIBUTE, page); + return COMMENT_EDIT_VIEW; + } + convert conv = new convert(); + comment.setTime(conv.dateToFormatDTOString(new Time(System.currentTimeMillis()))); + if (id <= 0) { + throw new IllegalArgumentException(); + } + comment.setPersonId(principal.getId()); + comment.setPostId(postId); + redirectAttributes.addAttribute(PAGE_ATTRIBUTE, page); + commentService.update(id, toCommentEntity(comment)); + return Constants.REDIRECT_VIEW + "/" + postId + "/" + COMMENT_VIEW; + } + + @PostMapping("{postId}/comments/delete/{id}") + public String deletePostComment( + @PathVariable(name = "id") Long id, + @PathVariable(name = "postId") Long postId, + @RequestParam(name = COMMENT_ATTRIBUTE, defaultValue = "0") int page, + @AuthenticationPrincipal UserPrincipal principal, + RedirectAttributes redirectAttributes) { + redirectAttributes.addAttribute(PAGE_ATTRIBUTE, page); + if (commentService.get(id).getperson().getId() == principal.getId()) + commentService.delete(id); + return Constants.REDIRECT_VIEW + "/" + postId + "/" + COMMENT_VIEW; + } +} diff --git a/5 lab/src/main/java/com/example/demo/posts/api/HomePostDto.java b/5 lab/src/main/java/com/example/demo/posts/api/HomePostDto.java new file mode 100644 index 0000000..6bcf786 --- /dev/null +++ b/5 lab/src/main/java/com/example/demo/posts/api/HomePostDto.java @@ -0,0 +1,82 @@ +package com.example.demo.posts.api; + +import com.example.demo.core.convert; +import com.fasterxml.jackson.annotation.JsonProperty; + +import com.fasterxml.jackson.annotation.JsonProperty.Access; + +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; + +public class HomePostDto { + convert conv = new convert(); + @JsonProperty(access = Access.READ_ONLY) + private Long id; + @Min(0) + private Long personId = 0L; + private String personUsername; + private String time = ""; + @NotBlank + private String text; + @Min(0) + private Long viewed = 0L; + @Min(0) + private Long liked = 0L; + + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getPersonId() { + return personId; + } + + public void setPersonId(Long personId) { + this.personId = personId; + } + + public String getPersonUsername() { + return personUsername; + } + + public void setPersonUsername(String personUsername) { + this.personUsername = personUsername; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + + public String getTime() { + return time; + } + + public void setTime(String time) { + this.time = time; + } + + public Long getViewed() { + return viewed; + } + + public void setViewed(Long viewed) { + this.viewed = viewed; + } + + public Long getLiked() { + return liked; + } + + public void setLiked(Long liked) { + this.liked = liked; + } +} diff --git a/5 lab/src/main/java/com/example/demo/posts/api/PostController.java b/5 lab/src/main/java/com/example/demo/posts/api/PostController.java new file mode 100644 index 0000000..6ab57c2 --- /dev/null +++ b/5 lab/src/main/java/com/example/demo/posts/api/PostController.java @@ -0,0 +1,154 @@ +package com.example.demo.posts.api; + +import java.util.Map; + +import org.modelmapper.ModelMapper; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.servlet.mvc.support.RedirectAttributes; + +import com.example.demo.core.convert; +import com.example.demo.core.api.PageAttributesMapper; +import com.example.demo.core.configuration.Constants; +import com.example.demo.posts.model.PostEntity; +import com.example.demo.posts.service.PostService; +import com.example.demo.users.service.UserService; + +import jakarta.validation.Valid; + +@Controller +@RequestMapping(PostController.URL) +public class PostController { + public static final String URL = Constants.ADMIN_PREFIX + "/post"; + private static final String POST_VIEW = "post"; + private static final String POST_EDIT_VIEW = "post-edit"; + private static final String PAGE_ATTRIBUTE = "page"; + private static final String POST_ATTRIBUTE = "post"; + + private final UserService userService; + private final PostService postService; + private final ModelMapper modelMapper; + + public PostController(PostService postService, UserService userService, ModelMapper modelMapper) { + this.postService = postService; + this.userService = userService; + this.modelMapper = modelMapper; + } + + private PostDto toDto(PostEntity entity) { + var model = modelMapper.map(entity, PostDto.class); + model.setPersonUsername(entity.getPerson().getUsername()); + convert time = new convert(); + model.setTime(time.dateToString(entity.getTime())); + return model; + } + + private PostEntity toEntity(PostDto dto) { + final PostEntity entity = modelMapper.map(dto, PostEntity.class); + entity.setPerson(userService.get(dto.getPersonId())); + return entity; + + } + + @GetMapping + public String getAll( + @RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page, + Model model) { + final Map attributes = PageAttributesMapper.toAttributes( + postService.getAll(page, Constants.DEFUALT_PAGE_SIZE), this::toDto); + model.addAllAttributes(attributes); + model.addAttribute(PAGE_ATTRIBUTE, page); + return POST_VIEW; + } + + @GetMapping("/edit/") + public String create( + @RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page, + Model model) { + model.addAttribute(POST_ATTRIBUTE, new PostDto()); + model.addAttribute(PAGE_ATTRIBUTE, page); + return POST_EDIT_VIEW; + } + + @PostMapping("/edit/") + public String create( + @RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page, + @ModelAttribute(name = POST_ATTRIBUTE) @Valid PostDto post, + BindingResult bindingResult, + Model model, + RedirectAttributes redirectAttributes) { + if (bindingResult.hasErrors()) { + model.addAttribute(PAGE_ATTRIBUTE, page); + return POST_EDIT_VIEW; + } + redirectAttributes.addAttribute(PAGE_ATTRIBUTE, page); + postService.create(toEntity(post)); + return Constants.REDIRECT_VIEW + URL; + } + + @PostMapping("/like/{id}") + public PostDto addLike(@PathVariable(name = "id") Long id) { + return toDto(postService.addLike(id)); + } + + @DeleteMapping("/like/{id}") + public PostDto removeLike(@PathVariable(name = "id") Long id) { + return toDto(postService.removeLiked(id)); + } + + @PostMapping("/view/{id}") + public PostDto addView(@PathVariable(name = "id") Long id) { + return toDto(postService.addView(id)); + } + + @GetMapping("/edit/{id}") + public String update( + @PathVariable(name = "id") Long id, + @RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page, + Model model) { + if (id <= 0) { + throw new IllegalArgumentException(); + } + model.addAttribute(POST_ATTRIBUTE, toDto(postService.get(id))); + model.addAttribute(PAGE_ATTRIBUTE, page); + return POST_EDIT_VIEW; + } + + @PostMapping("/edit/{id}") + public String update( + @PathVariable(name = "id") Long id, + @RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page, + @ModelAttribute(name = POST_ATTRIBUTE) @Valid PostDto post, + BindingResult bindingResult, + Model model, + RedirectAttributes redirectAttributes) { + if (bindingResult.hasErrors()) { + model.addAttribute(PAGE_ATTRIBUTE, page); + return POST_EDIT_VIEW; + } + if (id <= 0) { + throw new IllegalArgumentException(); + } + redirectAttributes.addAttribute(PAGE_ATTRIBUTE, page); + postService.update(id, toEntity(post)); + return Constants.REDIRECT_VIEW + URL; + } + + @PostMapping("/delete/{id}") + public String delete( + @PathVariable(name = "id") Long id, + @RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page, + RedirectAttributes redirectAttributes) { + redirectAttributes.addAttribute(PAGE_ATTRIBUTE, page); + postService.delete(id); + return Constants.REDIRECT_VIEW + URL; + } +} diff --git a/5 lab/src/main/java/com/example/demo/posts/api/PostDto.java b/5 lab/src/main/java/com/example/demo/posts/api/PostDto.java new file mode 100644 index 0000000..ec7d7ee --- /dev/null +++ b/5 lab/src/main/java/com/example/demo/posts/api/PostDto.java @@ -0,0 +1,85 @@ +package com.example.demo.posts.api; + +import com.example.demo.core.convert; +import com.fasterxml.jackson.annotation.JsonProperty; + +import com.fasterxml.jackson.annotation.JsonProperty.Access; + +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; + +public class PostDto { + convert conv = new convert(); + @JsonProperty(access = Access.READ_ONLY) + private Long id; + @NotNull + @Min(1) + private Long personId; + private String personUsername; + @NotBlank + private String time; + @NotBlank + private String text; + @Min(0) + private Long viewed; + @Min(0) + private Long liked; + + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getPersonId() { + return personId; + } + + public void setPersonId(Long personId) { + this.personId = personId; + } + + public String getPersonUsername() { + return personUsername; + } + + public void setPersonUsername(String personUsername) { + this.personUsername = personUsername; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + + public String getTime() { + return time; + } + + public void setTime(String time) { + this.time = time; + } + + public Long getViewed() { + return viewed; + } + + public void setViewed(Long viewed) { + this.viewed = viewed; + } + + public Long getLiked() { + return liked; + } + + public void setLiked(Long liked) { + this.liked = liked; + } +} diff --git a/5 lab/src/main/java/com/example/demo/posts/model/PostEntity.java b/5 lab/src/main/java/com/example/demo/posts/model/PostEntity.java new file mode 100644 index 0000000..0abfabe --- /dev/null +++ b/5 lab/src/main/java/com/example/demo/posts/model/PostEntity.java @@ -0,0 +1,106 @@ +package com.example.demo.posts.model; + +import java.util.Date; +import java.util.Objects; + +import org.hibernate.annotations.OnDelete; +import org.hibernate.annotations.OnDeleteAction; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; + +import com.example.demo.core.model.BaseEntity; +import com.example.demo.users.model.UserEntity; + +@Entity +@Table(name = "posts") +public class PostEntity extends BaseEntity { + @ManyToOne + @OnDelete(action = OnDeleteAction.CASCADE) + @JoinColumn(name = "user_Id", nullable = false) + private UserEntity person; + @Column(nullable = false) + private Date time; + @Column(length = 100) + private String text; + @Column + private Long viewed; + @Column + private Long liked; + + public PostEntity() { + super(); + } + + public PostEntity(UserEntity person, Date time, String text, Long viewed, Long liked) { + this.person = person; + this.text = text; + this.time = time; + this.viewed = viewed; + this.liked = liked; + } + + public UserEntity getPerson() { + return person; + } + + public void setPerson(UserEntity person) { + this.person = person; + } + + public Date getTime() { + return time; + } + + public void setTime(Date time) { + this.time = time; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + + public Long getViewed() { + return viewed; + } + + public void setViewed(Long viewed) { + this.viewed = viewed; + } + + public Long getLiked() { + return liked; + } + + public void setLiked(Long liked) { + this.liked = liked; + } + + @Override + public int hashCode() { + return Objects.hash(id, person, time, text, viewed, liked); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null || getClass() != obj.getClass()) + return false; + final PostEntity other = (PostEntity) obj; + return Objects.equals(other.getId(), id) + && Objects.equals(other.getPerson(), person) + && Objects.equals(other.getText(), text) + && Objects.equals(other.getTime(), time) + && Objects.equals(other.getLiked(), liked) + && Objects.equals(other.getViewed(), viewed); + } + +} diff --git a/5 lab/src/main/java/com/example/demo/posts/repository/PostRepository.java b/5 lab/src/main/java/com/example/demo/posts/repository/PostRepository.java new file mode 100644 index 0000000..f197162 --- /dev/null +++ b/5 lab/src/main/java/com/example/demo/posts/repository/PostRepository.java @@ -0,0 +1,21 @@ +package com.example.demo.posts.repository; + +import java.util.Optional; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.PagingAndSortingRepository; + +import com.example.demo.posts.model.PostEntity; + +public interface PostRepository + extends CrudRepository, PagingAndSortingRepository { + + @Query("SELECT p FROM PostEntity p ORDER BY p.id DESC") + Page findAllDesc(Pageable page); + + Optional findById(Long Id); + +} \ No newline at end of file diff --git a/5 lab/src/main/java/com/example/demo/posts/service/PostService.java b/5 lab/src/main/java/com/example/demo/posts/service/PostService.java new file mode 100644 index 0000000..8fb1a89 --- /dev/null +++ b/5 lab/src/main/java/com/example/demo/posts/service/PostService.java @@ -0,0 +1,88 @@ +package com.example.demo.posts.service; + +import java.util.List; +import java.util.stream.StreamSupport; +import java.sql.Time; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.example.demo.core.error.NotFoundException; +import com.example.demo.posts.model.PostEntity; +import com.example.demo.posts.repository.PostRepository; + +@Service +public class PostService { + private final PostRepository repository; + + public PostService(PostRepository repository) { + this.repository = repository; + } + + @Transactional(readOnly = true) + public List getAll() { + return StreamSupport.stream(repository.findAll().spliterator(), false).toList(); + } + + @Transactional(readOnly = true) + public Page getAll(int page, int size) { + return repository.findAllDesc(PageRequest.of(page, size)); + } + + @Transactional(readOnly = true) + public PostEntity get(Long id) { + return repository.findById(id) + .orElseThrow(() -> new NotFoundException(PostEntity.class, id)); + } + + @Transactional + public PostEntity create(PostEntity entity) { + if (entity == null) { + throw new IllegalArgumentException("Entity is null"); + } + entity.setTime(new Time(System.currentTimeMillis())); + repository.save(entity); + return repository.save(entity); + } + + @Transactional + public PostEntity addLike(Long id) { + final PostEntity existsEntity = get(id); + existsEntity.setLiked((existsEntity.getLiked() + 1)); + repository.save(existsEntity); + return existsEntity; + } + + @Transactional + public PostEntity removeLiked(Long id) { + final PostEntity existsEntity = get(id); + existsEntity.setLiked((existsEntity.getLiked() - 1)); + repository.save(existsEntity); + return existsEntity; + } + + @Transactional + public PostEntity addView(Long id) { + final PostEntity existsEntity = get(id); + existsEntity.setViewed((existsEntity.getViewed() + 1)); + repository.save(existsEntity); + return existsEntity; + } + + @Transactional + public PostEntity update(Long id, PostEntity entity) { + final PostEntity existsEntity = get(id); + existsEntity.setText(entity.getText()); + repository.save(existsEntity); + return existsEntity; + } + + @Transactional + public PostEntity delete(Long id) { + final PostEntity existsEntity = get(id); + repository.delete(existsEntity); + return existsEntity; + } +} diff --git a/5 lab/src/main/java/com/example/demo/users/api/FriendDto.java b/5 lab/src/main/java/com/example/demo/users/api/FriendDto.java new file mode 100644 index 0000000..c24e2e9 --- /dev/null +++ b/5 lab/src/main/java/com/example/demo/users/api/FriendDto.java @@ -0,0 +1,27 @@ +package com.example.demo.users.api; + +import com.example.demo.users.model.UserEntity; + +public class FriendDto { + private UserEntity user; + private int isFriend; + + public FriendDto() { + } + + public UserEntity getUser() { + return user; + } + + public void setUser(UserEntity user) { + this.user = user; + } + + public int getIsFriend() { + return isFriend; + } + + public void setIsFriend(int isFriend) { + this.isFriend = isFriend; + } +} diff --git a/5 lab/src/main/java/com/example/demo/users/api/UserController.java b/5 lab/src/main/java/com/example/demo/users/api/UserController.java new file mode 100644 index 0000000..89fcc64 --- /dev/null +++ b/5 lab/src/main/java/com/example/demo/users/api/UserController.java @@ -0,0 +1,144 @@ +package com.example.demo.users.api; + +import java.util.List; +import java.util.Map; + +import org.modelmapper.ModelMapper; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.servlet.mvc.support.RedirectAttributes; + +import com.example.demo.core.api.PageAttributesMapper; +import com.example.demo.core.configuration.Constants; +import com.example.demo.users.model.UserEntity; +import com.example.demo.users.model.UserTop; +import com.example.demo.users.service.UserService; + +import jakarta.validation.Valid; + +@Controller +@RequestMapping(UserController.URL) +public class UserController { + public static final String URL = Constants.ADMIN_PREFIX + "/user"; + private static final String USER_VIEW = "user"; + private static final String USER_EDIT_VIEW = "user-edit"; + private static final String PAGE_ATTRIBUTE = "page"; + private static final String USER_ATTRIBUTE = "user"; + + private final UserService userService; + private final ModelMapper modelMapper; + + public UserController(UserService userService, ModelMapper modelMapper) { + this.userService = userService; + this.modelMapper = modelMapper; + } + + private UserDto toDto(UserEntity entity) { + var model = modelMapper.map(entity, UserDto.class); + if (entity.getFriends() != null) { + for (UserEntity user : entity.getFriends()) { + model.addFriendId(user.getId()); + } + } + return model; + + } + + public UserTopDto toTopDto(UserTop user) { + return modelMapper.map(user, UserTopDto.class); + + } + + private UserEntity toEntity(UserDto dto) { + return modelMapper.map(dto, UserEntity.class); + } + + @GetMapping + public String getAll( + @RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page, + Model model) { + final Map attributes = PageAttributesMapper.toAttributes( + userService.getAll(page, Constants.DEFUALT_PAGE_SIZE), this::toDto); + model.addAllAttributes(attributes); + model.addAttribute(PAGE_ATTRIBUTE, page); + return USER_VIEW; + } + + @GetMapping("/edit/") + public String create( + @RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page, + Model model) { + model.addAttribute(USER_ATTRIBUTE, new UserDto()); + model.addAttribute(PAGE_ATTRIBUTE, page); + return USER_EDIT_VIEW; + } + + @PostMapping("/edit/") + public String create( + @RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page, + @ModelAttribute(name = USER_ATTRIBUTE) @Valid UserDto user, + BindingResult bindingResult, + Model model, + RedirectAttributes redirectAttributes) { + if (bindingResult.hasErrors()) { + model.addAttribute(PAGE_ATTRIBUTE, page); + return USER_EDIT_VIEW; + } + redirectAttributes.addAttribute(PAGE_ATTRIBUTE, page); + userService.create(toEntity(user)); + return Constants.REDIRECT_VIEW + URL; + } + + @GetMapping("/edit/{id}") + public String update( + @PathVariable(name = "id") Long id, + @RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page, + Model model) { + if (id <= 0) { + throw new IllegalArgumentException(); + } + model.addAttribute(USER_ATTRIBUTE, toDto(userService.get(id))); + model.addAttribute(PAGE_ATTRIBUTE, page); + return USER_EDIT_VIEW; + } + + @PostMapping("/edit/{id}") + public String update( + @PathVariable(name = "id") Long id, + @RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page, + @ModelAttribute(name = USER_ATTRIBUTE) @Valid UserDto user, + BindingResult bindingResult, + Model model, + RedirectAttributes redirectAttributes) { + if (id <= 0) { + throw new IllegalArgumentException(); + } + redirectAttributes.addAttribute(PAGE_ATTRIBUTE, page); + userService.update(id, toEntity(user)); + return Constants.REDIRECT_VIEW + URL; + } + + @PostMapping("/delete/{id}") + public String delete( + @PathVariable(name = "id") Long id, + @RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page, + RedirectAttributes redirectAttributes) { + redirectAttributes.addAttribute(PAGE_ATTRIBUTE, page); + userService.delete(id); + return Constants.REDIRECT_VIEW + URL; + } + + @GetMapping("/topFriends") + public List getMethod() { + return userService.getTop(0, 5).getContent().stream() + .map(this::toTopDto) + .toList(); + } +} diff --git a/5 lab/src/main/java/com/example/demo/users/api/UserDto.java b/5 lab/src/main/java/com/example/demo/users/api/UserDto.java new file mode 100644 index 0000000..1ee7121 --- /dev/null +++ b/5 lab/src/main/java/com/example/demo/users/api/UserDto.java @@ -0,0 +1,76 @@ +package com.example.demo.users.api; + +import java.util.ArrayList; +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonProperty.Access; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; + +public class UserDto { + @JsonProperty(access = Access.READ_ONLY) + private Long id; + @NotBlank + @Size(min = 3, max = 20) + private String username; + @NotBlank + @Size(min = 3, max = 20) + private String email; + @NotBlank + @Size(min = 3, max = 20) + private String password; + private String role; + private List friendsId = new ArrayList<>(); + + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + public List getFriendsId() { + return friendsId; + } + + public void addFriendId(long Id) { + friendsId.add(Id); + } + + public String getRole() { + return role; + } + + public void setRole(String role) { + this.role = role; + } +} diff --git a/5 lab/src/main/java/com/example/demo/users/api/UserProfileController.java b/5 lab/src/main/java/com/example/demo/users/api/UserProfileController.java new file mode 100644 index 0000000..798c503 --- /dev/null +++ b/5 lab/src/main/java/com/example/demo/users/api/UserProfileController.java @@ -0,0 +1,146 @@ +package com.example.demo.users.api; + +import java.util.Map; +import org.modelmapper.ModelMapper; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.servlet.mvc.support.RedirectAttributes; + +import com.example.demo.core.api.PageAttributesMapper; +import com.example.demo.core.configuration.Constants; +import com.example.demo.core.security.UserPrincipal; +import com.example.demo.users.model.FriendsEntity; +import com.example.demo.users.model.UserEntity; +import com.example.demo.users.service.UserService; + +import jakarta.validation.Valid; + +@Controller +@RequestMapping(UserProfileController.URL) +public class UserProfileController { + public static final String URL = "/profile"; + private static final String PROFILE_VIEW = "profile"; + private static final String FRIENDS_VIEW = "friends"; + private static final String PAGE_ATTRIBUTE = "page"; + private static final String USER_ATTRIBUTE = "user"; + + private final UserService userService; + private final ModelMapper modelMapper; + + public UserProfileController(UserService userService, ModelMapper modelMapper) { + this.userService = userService; + this.modelMapper = modelMapper; + } + + private UserProfileDto toDto(UserEntity entity) { + var model = modelMapper.map(entity, UserProfileDto.class); + if (entity.getFriends() != null) { + for (UserEntity user : entity.getFriends()) { + model.addFriendId(user.getId()); + } + } + return model; + + } + + private FriendDto toDto(FriendsEntity entity) { + var model = modelMapper.map(entity, FriendDto.class); + return model; + + } + + private UserEntity toEntity(UserProfileDto dto) { + return modelMapper.map(dto, UserEntity.class); + } + + @GetMapping + public String getProfile( + @RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page, + @AuthenticationPrincipal UserPrincipal principal, + Model model) { + var user = userService.get(principal.getId()); + model.addAttribute(USER_ATTRIBUTE, toDto(user)); + return PROFILE_VIEW; + } + + @PostMapping("/edit/{id}") + public String update( + @PathVariable(name = "id") Long id, + @ModelAttribute(name = USER_ATTRIBUTE) @Valid UserProfileDto user, + BindingResult bindingResult, + Model model, + RedirectAttributes redirectAttributes) { + if (user.getUsername().length() < 4) { + bindingResult.rejectValue("username", "profile:usernames", "Неправльный логин."); + model.addAttribute(USER_ATTRIBUTE, user); + return PROFILE_VIEW; + } + if (user.getEmail().length() < 4) { + bindingResult.rejectValue("email", "profile:emails", "Неправильная почта."); + model.addAttribute(USER_ATTRIBUTE, user); + return PROFILE_VIEW; + } + if (user.getPassword().length() < 4 || user.getPassword().length() > 30) { + bindingResult.rejectValue("password", "profile:passwords", "Неправильный пароль."); + model.addAttribute(USER_ATTRIBUTE, user); + return PROFILE_VIEW; + } + if (id <= 0) { + throw new IllegalArgumentException(); + } + userService.update(id, toEntity(user)); + return Constants.REDIRECT_VIEW + URL; + } + + @GetMapping("/{id}/friends") + public String getFriends( + @PathVariable(name = "id") Long id, + @RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page, + @AuthenticationPrincipal UserPrincipal principal, + Model model) { + Map attributes = PageAttributesMapper + .toAttributes(userService.getFriends(id, page, 5), + this::toDto); + + model.addAllAttributes(attributes); + model.addAttribute("principal", toDto(userService.get(principal.getId()))); + return FRIENDS_VIEW; + } + + @PostMapping("/{principalId}/friends/add/{id}") + public String addFriend( + @PathVariable(name = "id") Long id, + @PathVariable(name = "principalId") Long principalId, + @ModelAttribute(name = USER_ATTRIBUTE) @Valid UserProfileDto user, + @RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page, + @AuthenticationPrincipal UserPrincipal principal, + Model model, + RedirectAttributes redirectAttributes) { + redirectAttributes.addAttribute(PAGE_ATTRIBUTE, page); + userService.addFriend(principal.getId(), userService.get(id)); + return Constants.REDIRECT_VIEW + "/" + PROFILE_VIEW + "/" + principalId + "/" + FRIENDS_VIEW; + } + + @PostMapping("/{principalId}/friends/remove/{id}") + public String removeFriend( + @PathVariable(name = "id") Long id, + @PathVariable(name = "principalId") Long principalId, + @ModelAttribute(name = USER_ATTRIBUTE) @Valid UserProfileDto user, + @RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page, + @AuthenticationPrincipal UserPrincipal principal, + Model model, + RedirectAttributes redirectAttributes) { + redirectAttributes.addAttribute(PAGE_ATTRIBUTE, page); + userService.removeFriend(principal.getId(), userService.get(id)); + return Constants.REDIRECT_VIEW + "/" + PROFILE_VIEW + "/" + principalId + "/" + FRIENDS_VIEW; + } + +} diff --git a/5 lab/src/main/java/com/example/demo/users/api/UserProfileDto.java b/5 lab/src/main/java/com/example/demo/users/api/UserProfileDto.java new file mode 100644 index 0000000..ed27d6f --- /dev/null +++ b/5 lab/src/main/java/com/example/demo/users/api/UserProfileDto.java @@ -0,0 +1,67 @@ +package com.example.demo.users.api; + +import java.util.ArrayList; +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonProperty.Access; + +public class UserProfileDto { + @JsonProperty(access = Access.READ_ONLY) + private Long id; + private String username; + private String email; + private String password; + private String role; + private List friendsId = new ArrayList<>(); + + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + public List getFriendsId() { + return friendsId; + } + + public void addFriendId(long Id) { + friendsId.add(Id); + } + + public String getRole() { + return role; + } + + public void setRole(String role) { + this.role = role; + } +} diff --git a/5 lab/src/main/java/com/example/demo/users/api/UserSignupController.java b/5 lab/src/main/java/com/example/demo/users/api/UserSignupController.java new file mode 100644 index 0000000..a4fb233 --- /dev/null +++ b/5 lab/src/main/java/com/example/demo/users/api/UserSignupController.java @@ -0,0 +1,71 @@ +package com.example.demo.users.api; + +import java.util.Objects; + +import org.modelmapper.ModelMapper; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +import com.example.demo.core.configuration.Constants; +import com.example.demo.users.model.UserEntity; +import com.example.demo.users.service.UserService; + +import jakarta.validation.Valid; + +@Controller +@RequestMapping(UserSignupController.URL) +public class UserSignupController { + public static final String URL = "/signup"; + + private static final String SIGNUP_VIEW = "signup"; + private static final String USER_ATTRIBUTE = "user"; + + private final UserService userService; + private final ModelMapper modelMapper; + + public UserSignupController( + UserService userService, + ModelMapper modelMapper) { + this.userService = userService; + this.modelMapper = modelMapper; + } + + private UserEntity toEntity(UserSignupDto dto) { + return modelMapper.map(dto, UserEntity.class); + } + + @GetMapping + public String getSignup(Model model) { + model.addAttribute(USER_ATTRIBUTE, new UserSignupDto()); + return SIGNUP_VIEW; + } + + @PostMapping + public String signup( + @ModelAttribute(name = USER_ATTRIBUTE) @Valid UserSignupDto user, + BindingResult bindingResult, + Model model) { + if (bindingResult.hasErrors()) { + return SIGNUP_VIEW; + } + if (!Objects.equals(user.getPassword(), user.getPasswordConfirm())) { + bindingResult.rejectValue("password", "signup:passwords", "Пароли не совпадают."); + model.addAttribute(USER_ATTRIBUTE, user); + return SIGNUP_VIEW; + } + if (user.getUsername().equals("[deleted]")) { + bindingResult.rejectValue("username", "signup:usernames", "Запрещенный логин."); + model.addAttribute(USER_ATTRIBUTE, user); + return SIGNUP_VIEW; + } + var user2 = userService.create(toEntity(user)); + userService.addFriend(1L, user2); + return Constants.REDIRECT_VIEW + Constants.LOGIN_URL + "?signup"; + } + +} diff --git a/5 lab/src/main/java/com/example/demo/users/api/UserSignupDto.java b/5 lab/src/main/java/com/example/demo/users/api/UserSignupDto.java new file mode 100644 index 0000000..64e9b02 --- /dev/null +++ b/5 lab/src/main/java/com/example/demo/users/api/UserSignupDto.java @@ -0,0 +1,51 @@ +package com.example.demo.users.api; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; + +public class UserSignupDto { + @NotBlank + @Size(min = 3, max = 20) + private String username; + @NotBlank + @Size(min = 3, max = 20) + private String email; + @NotBlank + @Size(min = 3, max = 20) + private String password; + @NotBlank + @Size(min = 3, max = 20) + private String passwordConfirm; + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getPasswordConfirm() { + return passwordConfirm; + } + + public void setPasswordConfirm(String passwordConfirm) { + this.passwordConfirm = passwordConfirm; + } +} diff --git a/5 lab/src/main/java/com/example/demo/users/api/UserTopDto.java b/5 lab/src/main/java/com/example/demo/users/api/UserTopDto.java new file mode 100644 index 0000000..fe86cf7 --- /dev/null +++ b/5 lab/src/main/java/com/example/demo/users/api/UserTopDto.java @@ -0,0 +1,26 @@ +package com.example.demo.users.api; + +public class UserTopDto { + private String user; + private int count; + + public UserTopDto() { + } + + public String getUser() { + return user; + } + + public void setUser(String user) { + this.user = user; + } + + public int getCount() { + return count; + } + + public void setCount(int count) { + this.count = count; + } + +} diff --git a/5 lab/src/main/java/com/example/demo/users/model/FriendsEntity.java b/5 lab/src/main/java/com/example/demo/users/model/FriendsEntity.java new file mode 100644 index 0000000..5eae419 --- /dev/null +++ b/5 lab/src/main/java/com/example/demo/users/model/FriendsEntity.java @@ -0,0 +1,7 @@ +package com.example.demo.users.model; + +public interface FriendsEntity { + UserEntity getUser(); + + Long getIsFriend(); +} diff --git a/5 lab/src/main/java/com/example/demo/users/model/UserEntity.java b/5 lab/src/main/java/com/example/demo/users/model/UserEntity.java new file mode 100644 index 0000000..ab6cc57 --- /dev/null +++ b/5 lab/src/main/java/com/example/demo/users/model/UserEntity.java @@ -0,0 +1,114 @@ +package com.example.demo.users.model; + +import java.util.ArrayList; +import java.util.Objects; + +import org.hibernate.annotations.OnDelete; +import org.hibernate.annotations.OnDeleteAction; + +import java.util.List; +import com.example.demo.core.model.BaseEntity; + +import jakarta.persistence.CascadeType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.JoinTable; +import jakarta.persistence.ManyToMany; +import jakarta.persistence.Table; + +@Entity +@Table(name = "users") +public class UserEntity extends BaseEntity { + @Column(nullable = false, length = 20) + private String username; + @Column(nullable = false, length = 20) + private String email; + @Column(nullable = false) + private String password; + private UserRole role; + private Long isFriend; + @ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.REMOVE) + @OnDelete(action = OnDeleteAction.CASCADE) + @JoinTable(name = "user_friendship", joinColumns = @JoinColumn(name = "first_user_id", referencedColumnName = "Id", nullable = false), inverseJoinColumns = @JoinColumn(name = "second_user_id", referencedColumnName = "Id", nullable = false)) + private List friends = new ArrayList(); + + public UserEntity() { + } + + public UserEntity(String username, String email, String password, List friends) { + this.username = username; + this.email = email; + this.password = password; + this.friends = friends; + this.role = UserRole.USER; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public UserRole getRole() { + return role; + } + + public void setRole(UserRole role) { + this.role = role; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public List getFriends() { + return friends; + } + + public void setIsFriend(Long isFriend) { + this.isFriend = isFriend; + } + + public Long getIsFriend() { + return isFriend; + } + + public void setFriends(List friends) { + this.friends = friends; + } + + @Override + public int hashCode() { + return Objects.hash(id, username, email, password, friends); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null || getClass() != obj.getClass()) + return false; + final UserEntity other = (UserEntity) obj; + return Objects.equals(other.getId(), id) + && Objects.equals(other.getFriends(), friends) + && Objects.equals(other.getUsername(), username) + && Objects.equals(other.getEmail(), email) + && Objects.equals(other.getPassword(), password); + } +} \ No newline at end of file diff --git a/5 lab/src/main/java/com/example/demo/users/model/UserRole.java b/5 lab/src/main/java/com/example/demo/users/model/UserRole.java new file mode 100644 index 0000000..00e8efa --- /dev/null +++ b/5 lab/src/main/java/com/example/demo/users/model/UserRole.java @@ -0,0 +1,15 @@ +package com.example.demo.users.model; + +import org.springframework.security.core.GrantedAuthority; + +public enum UserRole implements GrantedAuthority { + ADMIN, + USER; + + private static final String PREFIX = "ROLE_"; + + @Override + public String getAuthority() { + return PREFIX + this.name(); + } +} diff --git a/5 lab/src/main/java/com/example/demo/users/model/UserTop.java b/5 lab/src/main/java/com/example/demo/users/model/UserTop.java new file mode 100644 index 0000000..8b8ade8 --- /dev/null +++ b/5 lab/src/main/java/com/example/demo/users/model/UserTop.java @@ -0,0 +1,7 @@ +package com.example.demo.users.model; + +public interface UserTop { + String getUser(); + + int getCount(); +} diff --git a/5 lab/src/main/java/com/example/demo/users/repository/UserRepository.java b/5 lab/src/main/java/com/example/demo/users/repository/UserRepository.java new file mode 100644 index 0000000..a768ff4 --- /dev/null +++ b/5 lab/src/main/java/com/example/demo/users/repository/UserRepository.java @@ -0,0 +1,38 @@ +package com.example.demo.users.repository; + +import java.util.Optional; +import java.util.List; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.PagingAndSortingRepository; +import org.springframework.data.repository.query.Param; + +import com.example.demo.users.model.FriendsEntity; +import com.example.demo.users.model.UserEntity; +import com.example.demo.users.model.UserTop; + +public interface UserRepository extends CrudRepository, + PagingAndSortingRepository { + List findByUsername(String username); + + Page findByUsername(String username, Pageable pageable); + + Optional findById(Long Id); + + @Query("SELECT distinct f FROM UserEntity u JOIN u.friends f WHERE f.id IN :friends") + Page findByFriends(@Param("friends") List friends, Pageable pageable); + + @Query("SELECT u as user, CASE WHEN :uId = f.id THEN 1 ELSE 0 END as isFriend FROM UserEntity u LEFT JOIN u.friends f Where :uId != u.id ") + Page findFriendsById(@Param("uId") Long id, Pageable pageable); + + Optional findByUsernameIgnoreCase(String username); + + @Query("select " + + " u.username as user, coalesce(size(u.friends), 0) as count " + + "from UserEntity u " + + "order by size(u.friends) desc") + Page getFiveWithTopFriends(Pageable pageable); +} diff --git a/5 lab/src/main/java/com/example/demo/users/service/Pageable.java b/5 lab/src/main/java/com/example/demo/users/service/Pageable.java new file mode 100644 index 0000000..1db9535 --- /dev/null +++ b/5 lab/src/main/java/com/example/demo/users/service/Pageable.java @@ -0,0 +1,5 @@ +package com.example.demo.users.service; + +public class Pageable { + +} diff --git a/5 lab/src/main/java/com/example/demo/users/service/UserService.java b/5 lab/src/main/java/com/example/demo/users/service/UserService.java new file mode 100644 index 0000000..cbaefa4 --- /dev/null +++ b/5 lab/src/main/java/com/example/demo/users/service/UserService.java @@ -0,0 +1,156 @@ +package com.example.demo.users.service; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import java.util.stream.StreamSupport; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; + +import com.example.demo.core.configuration.Constants; +import com.example.demo.core.error.NotFoundException; +import com.example.demo.core.security.UserPrincipal; +import com.example.demo.users.model.FriendsEntity; +import com.example.demo.users.model.UserEntity; +import com.example.demo.users.model.UserRole; +import com.example.demo.users.model.UserTop; +import com.example.demo.users.repository.UserRepository; + +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.StringUtils; + +@Service +public class UserService implements UserDetailsService { + private final UserRepository repository; + private final PasswordEncoder passwordEncoder; + + public UserService(UserRepository repository, PasswordEncoder passwordEncoder) { + this.passwordEncoder = passwordEncoder; + this.repository = repository; + } + + private void checkLogin(Long id, String login) { + final Optional existsUser = repository.findByUsernameIgnoreCase(login); + if (existsUser.isPresent() && !existsUser.get().getId().equals(id) + && existsUser.get().getUsername() != "[deleted]") { + throw new IllegalArgumentException( + String.format("User with login %s is already exists", login)); + } + } + + @Transactional(readOnly = true) + public List getAll() { + return StreamSupport.stream(repository.findAll().spliterator(), false).toList(); + } + + @Transactional(readOnly = true) + public Page getAll(int page, int size) { + return repository.findAll(PageRequest.of(page, size)); + } + + @Transactional(readOnly = true) + public Page getByFriends(List friends, int page, int size) { + List ids = new ArrayList(); + for (UserEntity user : friends) { + ids.add(user.getId()); + } + return repository.findByFriends(ids, PageRequest.of(page, size)); + } + + @Transactional(readOnly = true) + public Page getFriends(Long id, int page, int size) { + return repository.findFriendsById(id, PageRequest.of(page, size)); + } + + @Transactional(readOnly = true) + public UserEntity get(Long id) { + return repository.findById(id) + .orElseThrow(() -> new NotFoundException(UserEntity.class, id)); + } + + @Transactional(readOnly = true) + public UserEntity getByLogin(String login) { + return repository.findByUsernameIgnoreCase(login) + .orElseThrow(() -> new IllegalArgumentException("Invalid login")); + } + + @Transactional + public UserEntity create(UserEntity entity) { + if (entity == null) { + throw new IllegalArgumentException("Entity is null"); + } + checkLogin(null, entity.getUsername()); + final String password = Optional.ofNullable(entity.getPassword()).orElse(""); + entity.setPassword( + passwordEncoder.encode( + StringUtils.hasText(password.strip()) ? password : Constants.DEFAULT_PASSWORD)); + entity.setRole(Optional.ofNullable(entity.getRole()).orElse(UserRole.USER)); + return repository.save(entity); + } + + @Transactional + public UserEntity addFriend(Long id, UserEntity entity) { + final UserEntity existsEntity = get(id); + if (existsEntity.getFriends() == null) + existsEntity.setFriends(new ArrayList()); + existsEntity.getFriends().add(entity); + repository.save(existsEntity); + return existsEntity; + } + + @Transactional + public UserEntity removeFriend(Long id, UserEntity entity) { + final UserEntity existsEntity = get(id); + + existsEntity.getFriends().removeIf(friend -> friend.getId() == entity.getId()); + repository.save(existsEntity); + return existsEntity; + } + + @Override + @Transactional(readOnly = true) + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + final UserEntity existsUser = getByLogin(username); + if (username == "[deleted]") + return null; + return new UserPrincipal(existsUser); + } + + @Transactional + public UserEntity update(long id, UserEntity entity) { + final UserEntity existsEntity = get(id); + checkLogin(id, entity.getUsername()); + existsEntity.setUsername(entity.getUsername()); + existsEntity.setEmail(entity.getEmail()); + if ((entity.getPassword() != null) && (entity.getPassword().length() < 30)) + existsEntity.setPassword( + passwordEncoder.encode( + StringUtils.hasText(entity.getPassword().strip()) ? entity.getPassword() + : Constants.DEFAULT_PASSWORD)); + repository.save(existsEntity); + return existsEntity; + } + + @Transactional + public UserEntity delete(long id) { + final UserEntity existsEntity = get(id); + existsEntity.setUsername("[deleted]"); + existsEntity.setEmail("[deleted]"); + existsEntity.setPassword("[deleted]"); + existsEntity.setFriends(null); + repository.save(existsEntity); + return existsEntity; + } + + @Transactional(readOnly = true) + public Page getTop(int page, int size) { + return repository.getFiveWithTopFriends(PageRequest.of(page, size)); + } +} diff --git a/5 lab/src/main/resources/application.properties b/5 lab/src/main/resources/application.properties new file mode 100644 index 0000000..62ab433 --- /dev/null +++ b/5 lab/src/main/resources/application.properties @@ -0,0 +1,20 @@ +# Server +spring.main.banner-mode=off +server.port=8080 + +# Logger settings +# Available levels are: TRACE, DEBUG, INFO, WARN, ERROR, FATAL, OFF +logging.level.com.example.demo=DEBUG + +# JPA Settings +spring.datasource.url=jdbc:h2:file:./data +spring.datasource.username=sa +spring.datasource.password=password +spring.datasource.driver-class-name=org.h2.Driver +spring.jpa.hibernate.ddl-auto=create +spring.jpa.open-in-view=false +# spring.jpa.show-sql=true +# spring.jpa.properties.hibernate.format_sql=true + +# H2 console +spring.h2.console.enabled=true \ No newline at end of file diff --git a/5 lab/src/main/resources/public/css/style.css b/5 lab/src/main/resources/public/css/style.css new file mode 100644 index 0000000..a29389a --- /dev/null +++ b/5 lab/src/main/resources/public/css/style.css @@ -0,0 +1,256 @@ +html, +body { + height: 100%; +} + +h1 { + font-size: 1.5em; +} + +h2 { + font-size: 1.25em; +} + +h3 { + font-size: 1.1em; +} + +td form { + margin: 0; + padding: 0; + margin-top: -.25em; +} + +.button-fixed-width { + width: 150px; +} + +.button-link { + padding: 0; +} + +.invalid-feedback { + display: block; +} + +.w-10 { + width: 10% !important; +} + +.my-navbar { + background-color: #3c3c3c !important; +} + +.my-navbar .link a:hover { + text-decoration: underline; +} + +.my-navbar .logo { + width: 26px; + height: 26px; +} + +.my-footer { + background-color: #2c2c2c; + height: 32px; + color: rgba(255, 255, 255, 0.5); +} + +.return a { + margin-left: auto; + font-size: 20px; + height: 44px; + width: 180px; + vertical-align: middle; + background-color: #000000 +} + +.home { + height: 100%; +} + +.filters { + background-color: #000000; + position: -webkit-sticky; + position: sticky; + top: 0; + height: 100vh; +} + +.text-center { + margin-right: auto; + margin-left: auto; +} + +.background { + background-color: #CDECDE; +} + +.post { + margin-bottom: 10px; + max-width: 40%; + border: 1px solid #928e8e; +} + +.myBlock { + text-decoration: none; + color: gray; +} + +.myPdn { + margin-top: 3px; + margin-bottom: 3px; + margin-left: 3px; + margin-right: 3px; +} + +.myWid { + width: 80%; +} + +.medwidth { + max-width: 40%; +} + +.mybtn { + margin-bottom: 10px; +} + +@media (max-width: 700px) { + .list-group { + width: 250px; + } + + .chat { + height: 60vh; + overflow: auto; + } + + .list-group button { + font-size: 12px; + } +} + +@media (max-width: 300px) { + .list-group a { + font-size: 10px; + } + + .list-group h6 { + font-size: 10px; + } + + .list-group .postImage img { + width: 100px; + height: 100px; + } + + .list-group img { + width: 20px; + height: 20px; + } + + .list-group button { + font-size: 6px; + } +} + +.list-group { + width: 100%; +} + +.text-left { + margin-right: 0px; +} + +.blackback { + background-color: #000000; +} + +.d-flex p { + margin-top: 5px; +} + +.d-flex h4 { + margin-left: 20px; +} + +.return-text { + color: #fff; +} + +.container { + padding: 10px; +} + +.scrollable { + position: relative; + overflow-y: scroll; + -webkit-overflow-scrolling: touch; +} + +.chat { + border: 1px solid #333; + display: block; + width: 100%; + height: 65vh; + background: #FFFFFF; + color: #fff; + max-height: 85vh; + overflow: auto; +} + +.password-control { + background: url(https://snipp.ru/demo/495/view.svg) 0 0 no-repeat; + background-position: center; +} + +.password-control.view { + background-position: center !important; + background: url(https://snipp.ru/demo/495/no-view.svg) 0 0 no-repeat; +} + +#image-preview { + width: 200px; + height: 200px; +} + +.username-avatar p { + color: #FFFFFF; +} + +.message { + margin-bottom: 5px; + height: 100%; + font-size: 150%; + background: gray; + border-radius: 10px; +} + + +.messages { + width: 50%; +} + +.input { + font-family: arial; + font-size: 16px; + vertical-align: middle; + background: #333; + color: #fff; + display: inline-block; + margin: 1px; + height: 30px; +} + +.chat-form__input { + width: 79%; +} + +.chat-form__submit { + width: 18%; +} + +.dialog { + width: 100%; +} \ No newline at end of file diff --git a/5 lab/src/main/resources/public/eye.png b/5 lab/src/main/resources/public/eye.png new file mode 100644 index 0000000000000000000000000000000000000000..c97068ed01143188cae360519fe44c5764f36058 GIT binary patch literal 6739 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 S_3D41d_65=%|?V{%>Mu<^! + + + + + + + + + + \ No newline at end of file diff --git a/5 lab/src/main/resources/public/like.png b/5 lab/src/main/resources/public/like.png new file mode 100644 index 0000000000000000000000000000000000000000..51bb867feebe5c636e49026205037ac090b737e8 GIT binary patch literal 9327 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 YuMp^`Mf$ITAugEBQ(+u}=dVxy59O2t&;S4c literal 0 HcmV?d00001 diff --git a/5 lab/src/main/resources/templates/comment-edit.html b/5 lab/src/main/resources/templates/comment-edit.html new file mode 100644 index 0000000..8080518 --- /dev/null +++ b/5 lab/src/main/resources/templates/comment-edit.html @@ -0,0 +1,28 @@ + + + + + Редактировать комментарий + + + +

+
+
+ + +
+
+
+ + Отмена +
+
+
+ + + \ No newline at end of file diff --git a/5 lab/src/main/resources/templates/comment.html b/5 lab/src/main/resources/templates/comment.html new file mode 100644 index 0000000..9e68c67 --- /dev/null +++ b/5 lab/src/main/resources/templates/comment.html @@ -0,0 +1,45 @@ + + + + + Комментарии + + + +
+ +

Данные отсутствуют

+ +

Комментарии

+ + + + + + + + + + + + + + + + + + + + +
IDПользовательТекстДата отправки
+
+ +
+
+
+
+
+ + + \ No newline at end of file diff --git a/5 lab/src/main/resources/templates/comments.html b/5 lab/src/main/resources/templates/comments.html new file mode 100644 index 0000000..0273482 --- /dev/null +++ b/5 lab/src/main/resources/templates/comments.html @@ -0,0 +1,58 @@ + + + + +
+ + +
+
+
+ david +

[[${comment.personUsername}]] +

+
+ [[${comment.time}]] +
+
+
+
[[${comment.text}]]
+
+
+
+
+ + +
+
+ + +
+
+
+
+
+
+ + +
+ + +
+
+ Вернуться + + +
+ + + \ No newline at end of file diff --git a/5 lab/src/main/resources/templates/default.html b/5 lab/src/main/resources/templates/default.html new file mode 100644 index 0000000..1601fae --- /dev/null +++ b/5 lab/src/main/resources/templates/default.html @@ -0,0 +1,78 @@ + + + + + + + + My network + + + + + + + + +
+
+
+ Автор, [[${#dates.year(#dates.createNow())}]] +
+ + + \ No newline at end of file diff --git a/5 lab/src/main/resources/templates/dialog.html b/5 lab/src/main/resources/templates/dialog.html new file mode 100644 index 0000000..cd2dbc2 --- /dev/null +++ b/5 lab/src/main/resources/templates/dialog.html @@ -0,0 +1,66 @@ + + + + + +
+
+
+
+ david +
+

[[${user.username}]]

+
+ +
+
+
+ + +
+
+

[[${message.text}]]

+

[[${message.time}]]

+
+
+
+
+

[[${message.text}]]

+

[[${message.time}]]

+
+
+
+
+
+ + +
+
+
+ +
+ + +
+
+ + +
+ +
+
+
+
+ + + \ No newline at end of file diff --git a/5 lab/src/main/resources/templates/dialogs.html b/5 lab/src/main/resources/templates/dialogs.html new file mode 100644 index 0000000..e098911 --- /dev/null +++ b/5 lab/src/main/resources/templates/dialogs.html @@ -0,0 +1,38 @@ + + + + + +
+ + + +
+ + +
+
+ david +

[[${user.username}]]

+
+ +
+
+
+

+
+
+
+
+
+
+ + +
+ + + \ No newline at end of file diff --git a/5 lab/src/main/resources/templates/error.html b/5 lab/src/main/resources/templates/error.html new file mode 100644 index 0000000..faa6b0a --- /dev/null +++ b/5 lab/src/main/resources/templates/error.html @@ -0,0 +1,37 @@ + + + + + Ошибка + + + +
+
    + +
  • + Неизвестная ошибка +
  • +
    + +
  • + Ошибка: [[${message}]] +
  • +
    + +
  • + Адрес: [[${url}]] +
  • +
  • + Класс исключения: [[${exception}]] +
  • +
  • + [[${method}]] ([[${file}]]:[[${line}]]) +
  • +
    +
+ На главную +
+ + + \ No newline at end of file diff --git a/5 lab/src/main/resources/templates/friends.html b/5 lab/src/main/resources/templates/friends.html new file mode 100644 index 0000000..2425d97 --- /dev/null +++ b/5 lab/src/main/resources/templates/friends.html @@ -0,0 +1,54 @@ + + + + +
+ + +
+
+
+ avatar + +

[[${user.user.username}]]

+
+ +
+
+ + +
+
+
+
+ + +
+
+
+
Это вы, самый крутой
+
+ +
+
+
+ Назад + + +
+ + + \ No newline at end of file diff --git a/5 lab/src/main/resources/templates/home.html b/5 lab/src/main/resources/templates/home.html new file mode 100644 index 0000000..da26dbf --- /dev/null +++ b/5 lab/src/main/resources/templates/home.html @@ -0,0 +1,72 @@ + + + + + Главная + + + +
+
+ + +
+ + +
+
+
+ AvatarImage +

[[${post.personUsername}]]

+
+ [[${post.time}]] +
+
+
+

[[${post.text}]]

+
+
+ + +
+
[[${post.liked}]]
+
+
+ Просмотры +
[[${post.viewed}]]
+
+
+
+ +
+
+
+
+ + +
+
+ + +
+
+
+
+
+ +
+ + +
+ + + \ No newline at end of file diff --git a/5 lab/src/main/resources/templates/login.html b/5 lab/src/main/resources/templates/login.html new file mode 100644 index 0000000..e1452a7 --- /dev/null +++ b/5 lab/src/main/resources/templates/login.html @@ -0,0 +1,44 @@ + + + + + Вход + + + +
+
+
+ Неверный логин или пароль +
+
+ Выход успешно произведен +
+
+ Пользователь успешно создан +
+
+ + +
+
+ + +
+
+ + +
+
+ + Регистрация +
+
+
+ + + + + \ No newline at end of file diff --git a/5 lab/src/main/resources/templates/message.html b/5 lab/src/main/resources/templates/message.html new file mode 100644 index 0000000..7fec29e --- /dev/null +++ b/5 lab/src/main/resources/templates/message.html @@ -0,0 +1,47 @@ + + + + + Сообщения + + + +
+ +

Сообщения

+ +

Списки постов

+ + + + + + + + + + + + + + + + + + + + + + +
IDОтправительПолучательТекст сообщенияДата отправки
+
+ +
+
+
+
+
+ + + \ No newline at end of file diff --git a/5 lab/src/main/resources/templates/pagination.html b/5 lab/src/main/resources/templates/pagination.html new file mode 100644 index 0000000..b11664a --- /dev/null +++ b/5 lab/src/main/resources/templates/pagination.html @@ -0,0 +1,51 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/5 lab/src/main/resources/templates/post-edit.html b/5 lab/src/main/resources/templates/post-edit.html new file mode 100644 index 0000000..7f2d320 --- /dev/null +++ b/5 lab/src/main/resources/templates/post-edit.html @@ -0,0 +1,26 @@ + + + + + Редактировать пост + + + +
+
+ +
+ + +
+
+
+ + Отмена +
+
+
+ + + \ No newline at end of file diff --git a/5 lab/src/main/resources/templates/post.html b/5 lab/src/main/resources/templates/post.html new file mode 100644 index 0000000..4d5063d --- /dev/null +++ b/5 lab/src/main/resources/templates/post.html @@ -0,0 +1,53 @@ + + + + + Посты + + + +
+ +

Данные отсутствуют

+ +

Посты

+ + + + + + + + + + + + + + + + + + + + + + + + +
IDТекстПользовательВремяКоличество просмотровКоличество лайков
+
+ +
+
+
+ + +
+ + + \ No newline at end of file diff --git a/5 lab/src/main/resources/templates/profile.html b/5 lab/src/main/resources/templates/profile.html new file mode 100644 index 0000000..af55f9b --- /dev/null +++ b/5 lab/src/main/resources/templates/profile.html @@ -0,0 +1,59 @@ + + + + + +
+ + +
+
+
+
+ account +

[[${user.username}]]

+
+
+
+
+
+
+ + +
+
+
+
+ + +
+
+
+ + +
+
+
+
+ +
+
+
+
+ +
+
+
+ +
+
+
+ + + \ No newline at end of file diff --git a/5 lab/src/main/resources/templates/signup.html b/5 lab/src/main/resources/templates/signup.html new file mode 100644 index 0000000..d6baaee --- /dev/null +++ b/5 lab/src/main/resources/templates/signup.html @@ -0,0 +1,46 @@ + + + + + Регистрация + + + +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+
+ + Отмена +
+
+
+ + + + + \ No newline at end of file diff --git a/5 lab/src/main/resources/templates/user-edit.html b/5 lab/src/main/resources/templates/user-edit.html new file mode 100644 index 0000000..2559370 --- /dev/null +++ b/5 lab/src/main/resources/templates/user-edit.html @@ -0,0 +1,36 @@ + + + + + Редактировать пользователя + + + +
+
+
+ + +
+
+ + +
+
+
+ + +
+
+
+ + Отмена +
+
+
+ + + \ No newline at end of file diff --git a/5 lab/src/main/resources/templates/user.html b/5 lab/src/main/resources/templates/user.html new file mode 100644 index 0000000..46612b1 --- /dev/null +++ b/5 lab/src/main/resources/templates/user.html @@ -0,0 +1,59 @@ + + + + + Пользователи + + + +
+ +

Данные отсутствуют

+ +

Пользователи

+ + + + + + + + + + + + + + + + + + + + +
IDИмя пользователяПочта пользователя
+
+
+ + +
+
+
+
+
+ + +
+
+
+
+ + +
+ + + \ No newline at end of file diff --git a/5 lab/src/test/resources/application.properties b/5 lab/src/test/resources/application.properties new file mode 100644 index 0000000..d5f355c --- /dev/null +++ b/5 lab/src/test/resources/application.properties @@ -0,0 +1,14 @@ +# Server +spring.main.banner-mode=off + +# Logger settings +# Available levels are: TRACE, DEBUG, INFO, WARN, ERROR, FATAL, OFF +logging.level.com.example.demo=DEBUG + +# JPA Settings +spring.datasource.url=jdbc:h2:mem:testdb +spring.datasource.username=sa +spring.datasource.password=password +spring.datasource.driver-class-name=org.h2.Driver +spring.jpa.hibernate.ddl-auto=create +spring.jpa.open-in-view=false \ No newline at end of file