├── .github
└── ISSUE_TEMPLATE
│ ├── -проблем-с-проект.md
│ ├── бъг.md
│ └── желание-или-идея.md
├── README.md
├── books-js-client
├── .gitignore
├── app.js
├── index.html
└── styles.css
├── books-server
├── .gitignore
├── build.gradle
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
└── src
│ ├── main
│ ├── java
│ │ └── bg
│ │ │ └── softuni
│ │ │ └── booksserver
│ │ │ ├── BooksServerApplication.java
│ │ │ ├── init
│ │ │ └── DBInit.java
│ │ │ ├── model
│ │ │ ├── dto
│ │ │ │ ├── AuthorDTO.java
│ │ │ │ └── BookDTO.java
│ │ │ └── entity
│ │ │ │ ├── AuthorEntity.java
│ │ │ │ └── BookEntity.java
│ │ │ ├── repository
│ │ │ ├── AuthorRepository.java
│ │ │ └── BookRepository.java
│ │ │ ├── service
│ │ │ ├── BookService.java
│ │ │ └── impl
│ │ │ │ └── BookServiceImpl.java
│ │ │ └── web
│ │ │ └── BooksRestController.java
│ └── resources
│ │ └── application.yaml
│ └── test
│ └── java
│ └── bg
│ └── softuni
│ └── booksserver
│ └── BooksServerApplicationTests.java
├── boot-demo
├── .gitignore
├── build.gradle
├── deps.txt
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
└── src
│ ├── main
│ ├── java
│ │ └── org
│ │ │ └── softuni
│ │ │ └── bootdemo
│ │ │ ├── BootDemoApplication.java
│ │ │ ├── util
│ │ │ └── CLGreeter.java
│ │ │ └── web
│ │ │ └── HelloController.java
│ └── resources
│ │ ├── application.yaml
│ │ └── templates
│ │ └── test.html
│ └── test
│ └── java
│ └── org
│ └── softuni
│ └── bootdemo
│ └── BootDemoApplicationTests.java
├── cache-redis
├── .gitignore
├── build.gradle
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
└── src
│ ├── main
│ ├── java
│ │ └── bg
│ │ │ └── softuni
│ │ │ └── cache
│ │ │ └── redis
│ │ │ ├── CacheRedisApplication.java
│ │ │ ├── config
│ │ │ └── RedisConfiguration.java
│ │ │ ├── inti
│ │ │ └── Init.java
│ │ │ ├── model
│ │ │ └── StudentDTO.java
│ │ │ └── service
│ │ │ └── StudentService.java
│ └── resources
│ │ └── application.yaml
│ └── test
│ └── java
│ └── bg
│ └── softuni
│ └── cache
│ └── redis
│ └── CacheRedisApplicationTests.java
├── dep-inj
├── .gitignore
├── build.gradle
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
└── src
│ └── main
│ ├── java
│ └── org
│ │ └── softuni
│ │ ├── Main.java
│ │ ├── model
│ │ └── User.java
│ │ ├── repository
│ │ ├── FileBasedUserRepository.java
│ │ ├── InMemoryUserRepository.java
│ │ └── UserRepository.java
│ │ └── service
│ │ ├── UserService.java
│ │ └── UserServiceImpl.java
│ └── resources
│ └── users.csv
├── errors
├── .gitignore
├── build.gradle
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
└── src
│ ├── main
│ ├── java
│ │ └── bg
│ │ │ └── softuni
│ │ │ └── errors
│ │ │ ├── ErrorsApplication.java
│ │ │ ├── config
│ │ │ └── ErrorConfig.java
│ │ │ ├── exception
│ │ │ ├── ObjectNotFoundException.java
│ │ │ └── ObjectNotFoundRestException.java
│ │ │ └── web
│ │ │ ├── GloabalNotFoundExceptionHandler.java
│ │ │ ├── NotFound1Controller.java
│ │ │ ├── NotFound2Controller.java
│ │ │ ├── NotFound3Controller.java
│ │ │ ├── TestRestController.java
│ │ │ └── WhitelabelTestController.java
│ └── resources
│ │ ├── application.yaml
│ │ └── templates
│ │ ├── error
│ │ └── 404.html
│ │ ├── iae.html
│ │ ├── notfound-global.html
│ │ └── notfound1.html
│ └── test
│ └── java
│ └── bg
│ └── softuni
│ └── errors
│ └── ErrorsApplicationTests.java
├── html
├── .gitignore
├── home.html
├── index.html
├── simplejs.html
└── test.requests.http
├── i18n
├── .gitignore
├── build.gradle
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
└── src
│ ├── main
│ ├── java
│ │ └── bg
│ │ │ └── softuni
│ │ │ └── i18n
│ │ │ ├── I18nApplication.java
│ │ │ └── web
│ │ │ ├── LangControllerCookies.java
│ │ │ └── LangControllerSessions.java
│ └── resources
│ │ ├── application.properties
│ │ └── templates
│ │ ├── lang-view-cookie.html
│ │ └── lang-view-session.html
│ └── test
│ └── java
│ └── bg
│ └── softuni
│ └── i18n
│ └── I18nApplicationTests.java
├── interview-questions
├── .gitignore
├── .idea
│ └── .gitignore
├── advanced
│ ├── 01-rest-api.md
│ ├── 02-spring-security.md
│ ├── 03-error-handling.md
│ ├── 04-events.md
│ ├── 05-testing.md
│ ├── 06-deployment-monitoring.md
│ ├── 07-aop.md
│ └── 08-kafka.md
└── fundamentals
│ ├── 01-internet-explained.md
│ ├── 02-http-protocol.md
│ ├── 03-spring-boot.md
│ ├── 04-spring-mvc.md
│ ├── 06-state.md
│ ├── 07-thymeleaf.md
│ └── 08-spring-essentials.md
├── kafka-consumer
├── .gitignore
├── build.gradle
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
└── src
│ ├── main
│ ├── java
│ │ └── bg
│ │ │ └── softuni
│ │ │ └── kafka
│ │ │ ├── KafkaConsumerApplication.java
│ │ │ ├── config
│ │ │ └── KafkaConfig.java
│ │ │ ├── consumer
│ │ │ └── ExRatesConsumer.java
│ │ │ └── model
│ │ │ └── ExchangeRatesDTO.java
│ └── resources
│ │ └── application.yaml
│ └── test
│ └── java
│ └── bg
│ └── softuni
│ └── kafka
│ └── KafkaConsumerApplicationTests.java
├── kafka-producer
├── .gitignore
├── build.gradle
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
└── src
│ ├── main
│ ├── java
│ │ └── bg
│ │ │ └── softuni
│ │ │ └── kafka
│ │ │ ├── KafkaApplication.java
│ │ │ ├── config
│ │ │ └── KafkaConfig.java
│ │ │ ├── model
│ │ │ └── ExchangeRatesDTO.java
│ │ │ ├── service
│ │ │ └── KafkaPublicationService.java
│ │ │ └── web
│ │ │ └── FakePublisherController.java
│ └── resources
│ │ └── application.yaml
│ └── test
│ └── java
│ └── bg
│ └── softuni
│ └── kafka
│ └── KafkaApplicationTests.java
├── local
├── docker-mysql-apple-m1.yaml
└── prometheus.yml
├── mobilele
├── .gitignore
├── build.gradle
├── deployment
│ └── Dockerfile
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── sample-docker-commands.md
├── settings.gradle
└── src
│ ├── main
│ ├── java
│ │ └── org
│ │ │ └── softuni
│ │ │ └── mobilele
│ │ │ ├── MobileleApplication.java
│ │ │ ├── config
│ │ │ ├── AppConfig.java
│ │ │ ├── MailConfiguration.java
│ │ │ ├── OpenExchangeRateConfig.java
│ │ │ ├── ReCaptchaConfig.java
│ │ │ └── SecurityConfiguration.java
│ │ │ ├── init
│ │ │ └── RatesInit.java
│ │ │ ├── model
│ │ │ ├── dto
│ │ │ │ ├── BrandDTO.java
│ │ │ │ ├── ConvertRequestDTO.java
│ │ │ │ ├── CreateOfferDTO.java
│ │ │ │ ├── ExchangeRatesDTO.java
│ │ │ │ ├── ModelDTO.java
│ │ │ │ ├── MoneyDTO.java
│ │ │ │ ├── OfferDetailDTO.java
│ │ │ │ ├── OfferSummaryDTO.java
│ │ │ │ ├── ReCaptchaResponseDTO.java
│ │ │ │ └── UserRegistrationDTO.java
│ │ │ ├── entity
│ │ │ │ ├── BaseEntity.java
│ │ │ │ ├── BrandEntity.java
│ │ │ │ ├── ExchangeRateEntity.java
│ │ │ │ ├── ModelEntity.java
│ │ │ │ ├── OfferEntity.java
│ │ │ │ ├── UserActivationCodeEntity.java
│ │ │ │ ├── UserEntity.java
│ │ │ │ └── UserRoleEntity.java
│ │ │ ├── enums
│ │ │ │ ├── EngineEnum.java
│ │ │ │ ├── ModelCategoryEnum.java
│ │ │ │ ├── TransmissionEnum.java
│ │ │ │ └── UserRoleEnum.java
│ │ │ ├── events
│ │ │ │ └── UserRegisteredEvent.java
│ │ │ └── validation
│ │ │ │ ├── FieldMatch.java
│ │ │ │ ├── FieldMatchValidator.java
│ │ │ │ ├── UniqueUserEmail.java
│ │ │ │ ├── UniqueUserEmailValidator.java
│ │ │ │ ├── YearNotInTheFuture.java
│ │ │ │ └── YearNotInTheFutureValidator.java
│ │ │ ├── repository
│ │ │ ├── BrandRepository.java
│ │ │ ├── ExchangeRateRepository.java
│ │ │ ├── ModelRepository.java
│ │ │ ├── OfferRepository.java
│ │ │ ├── UserActivationCodeRepository.java
│ │ │ ├── UserRepository.java
│ │ │ └── UserRoleRepository.java
│ │ │ ├── service
│ │ │ ├── BrandService.java
│ │ │ ├── CurrencyService.java
│ │ │ ├── EmailService.java
│ │ │ ├── MonitoringService.java
│ │ │ ├── OfferService.java
│ │ │ ├── ReCaptchaService.java
│ │ │ ├── UserActivationService.java
│ │ │ ├── UserService.java
│ │ │ ├── aop
│ │ │ │ ├── MonitoringAspect.java
│ │ │ │ ├── Pointcuts.java
│ │ │ │ └── WarnIfExecutionExceeds.java
│ │ │ ├── exception
│ │ │ │ └── ObjectNotFoundException.java
│ │ │ ├── impl
│ │ │ │ ├── BrandServiceImpl.java
│ │ │ │ ├── CurrencyServiceImpl.java
│ │ │ │ ├── EmailServiceImpl.java
│ │ │ │ ├── MobileleUserDetailsService.java
│ │ │ │ ├── MonitoringServiceImpl.java
│ │ │ │ ├── OfferServiceImpl.java
│ │ │ │ ├── ReCaptchaServiceImpl.java
│ │ │ │ ├── UserActivationServiceImpl.java
│ │ │ │ └── UserServiceImpl.java
│ │ │ ├── oauth
│ │ │ │ └── OAuthSuccessHandler.java
│ │ │ └── schedulers
│ │ │ │ └── ActivationLinkCleanupScheduler.java
│ │ │ └── web
│ │ │ ├── BrandController.java
│ │ │ ├── CurrencyRestController.java
│ │ │ ├── OfferController.java
│ │ │ ├── OffersController.java
│ │ │ ├── UserLoginController.java
│ │ │ └── UserRegistrationController.java
│ └── resources
│ │ ├── application.yaml
│ │ ├── data.sql
│ │ ├── static
│ │ ├── css
│ │ │ ├── bootstrap.min.css
│ │ │ ├── main.css
│ │ │ └── reset-css.css
│ │ ├── favicon.ico
│ │ ├── images
│ │ │ ├── 1.jpg
│ │ │ ├── broken-car.png
│ │ │ ├── car-dealership-car-car-showroom.jpg
│ │ │ ├── car.png
│ │ │ └── user-avatar.svg
│ │ └── js
│ │ │ ├── bootstrap.min.js
│ │ │ └── jquery-3.5.1.slim.min.js
│ │ └── templates
│ │ ├── auth-login.html
│ │ ├── auth-register.html
│ │ ├── brands.html
│ │ ├── details.html
│ │ ├── email
│ │ └── registration-email.html
│ │ ├── error
│ │ ├── 404.html
│ │ └── 500.html
│ │ ├── fragments
│ │ ├── head.html
│ │ └── navbar.html
│ │ ├── index.html
│ │ ├── offer-add.html
│ │ ├── offers.html
│ │ └── update.html
│ └── test
│ ├── java
│ └── org
│ │ └── softuni
│ │ └── mobilele
│ │ ├── MobileleApplicationTests.java
│ │ ├── service
│ │ └── impl
│ │ │ ├── CurrencyServiceImplTestIT.java
│ │ │ └── MobileleUserDetailsServiceTest.java
│ │ ├── testutils
│ │ ├── DBInit.java
│ │ ├── TestDataUtil.java
│ │ └── UserTestDataUtil.java
│ │ └── web
│ │ ├── CurrencyRestControllerTestIT.java
│ │ ├── OfferControllerTestIT.java
│ │ └── UserRegistrationControllerTestIT.java
│ └── resources
│ └── application.yaml
├── mvcfn
├── .gitignore
├── build.gradle
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
└── src
│ ├── main
│ ├── java
│ │ └── org
│ │ │ └── softuni
│ │ │ └── mvcfn
│ │ │ ├── MvcfnApplication.java
│ │ │ ├── config
│ │ │ └── RoutesConfig.java
│ │ │ └── web
│ │ │ └── TestHandlers.java
│ └── resources
│ │ ├── application.properties
│ │ └── templates
│ │ └── test.html
│ └── test
│ └── java
│ └── org
│ └── softuni
│ └── mvcfn
│ └── MvcfnApplicationTests.java
├── plain-web-app
├── .gitignore
├── build.gradle
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
└── src
│ └── main
│ ├── java
│ └── bg
│ │ └── softuni
│ │ └── plainwebapp
│ │ ├── HelloFilter.java
│ │ └── HelloServlet.java
│ └── webapp
│ ├── WEB-INF
│ └── web.xml
│ └── index.jsp
├── proxies
├── .gitignore
├── build.gradle
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
└── src
│ └── main
│ └── java
│ └── bg
│ └── softuni
│ └── proxies
│ ├── Cacheable.java
│ ├── CacheableInvocationHandler.java
│ ├── Main.java
│ ├── StudentDTO.java
│ ├── StudentService.java
│ └── StudentServiceIfc.java
└── thymeleaf-pure
├── .gitignore
├── build.gradle
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
└── src
└── main
├── java
└── org
│ └── example
│ └── Main.java
└── resources
└── test.html
/.github/ISSUE_TEMPLATE/-проблем-с-проект.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: " Проблем с проект"
3 | about: Моля, използвайте при въпроси за вашите проекти
4 | title: ''
5 | labels: project-help
6 | assignees: luchob
7 |
8 | ---
9 |
10 | *Връзка към проекта:*
11 |
12 | _Поставете [връзка](https://github.com/) към проекта си тук._
13 |
14 | *Кратко описание:*
15 |
16 | Възможно най-накратко опишете:
17 |
18 | 1. Какво очаквате да се случи
19 | 2. Какво всъщност се случва
20 | 3. Какво опитахте
21 |
22 | *Стъпки за репродуциране:*
23 |
24 | Опишете възможно най-лесните стъпки, с помощта на които може да се репродуцира проблемът.
25 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/бъг.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Бъг
3 | about: Съобщете за бъг в нашите проекти
4 | title: ''
5 | labels: bug
6 | assignees: luchob
7 |
8 | ---
9 |
10 | **Опишете грешката**
11 |
12 | Опишете къде сме допуснали грешка.
13 | Какво очаквате да се случи, и какво се случва реално.
14 |
15 | **Стъпки за да се репродуцира**
16 |
17 | Ако описанието е недостатъчно, моля добавете тук.
18 |
19 | **Допълнителен контекст**
20 | Всякаква полезна информация - например OS, браузър и т.н.
21 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/желание-или-идея.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Желание или идея
3 | about: Предложете нещо свежо за нашия курс
4 | title: ''
5 | labels: suggestion
6 | assignees: luchob
7 |
8 | ---
9 |
10 | Опишете накратко какво бихте искали да видите или да чуете в нашия курс.
11 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Welcome
2 |
3 | This repository contains exersices and examples from
4 | the lectures Spring Fundametals and Spring Advanced in
5 | SoftUni.
6 |
7 | If you have troubles please [submit an issue](https://github.com/luchob/softuni-sep-2023/issues).
8 |
--------------------------------------------------------------------------------
/books-js-client/.gitignore:
--------------------------------------------------------------------------------
1 | ### IntelliJ IDEA ###
2 | .idea
3 | *.iws
4 | *.iml
5 | *.ipr
6 |
--------------------------------------------------------------------------------
/books-js-client/app.js:
--------------------------------------------------------------------------------
1 | let reloadBooksButton = document.getElementById('reloadBooks');
2 |
3 | reloadBooksButton.addEventListener('click', reloadBooks)
4 |
5 | function reloadBooks() {
6 |
7 | let booksContainer = document.getElementById('books-container');
8 | booksContainer.innerHTML = ''
9 |
10 | fetch('http://localhost:8080/api/books')
11 | .then(response => response.json())
12 | .then(json => json.forEach(book => {
13 |
14 | let bookRow = document.createElement('tr')
15 |
16 | let titleCol = document.createElement('td')
17 | let authorCol = document.createElement('td')
18 | let isbnCol = document.createElement('td')
19 | let actionCol= document.createElement('td')
20 |
21 | titleCol.textContent = book.title
22 | authorCol.textContent = book.author.name
23 | isbnCol.textContent = book.isbn
24 |
25 | let deleteBookBtn = document.createElement('button')
26 | deleteBookBtn.innerHTML = 'DELETE'
27 | deleteBookBtn.dataset.id = book.id
28 | deleteBookBtn.addEventListener('click', deleteBtnClicked)
29 |
30 | actionCol.appendChild(deleteBookBtn)
31 |
32 | bookRow.appendChild(titleCol)
33 | bookRow.appendChild(authorCol)
34 | bookRow.appendChild(isbnCol)
35 | bookRow.appendChild(actionCol)
36 |
37 | booksContainer.append(bookRow)
38 | }))
39 |
40 | }
41 |
42 | function deleteBtnClicked(event) {
43 |
44 | let bookId = event.target.dataset.id;
45 | let requestOptions = {
46 | method: 'DELETE'
47 | }
48 |
49 | fetch(`http://localhost:8080/api/books/${bookId}`, requestOptions)
50 | .then(_ => reloadBooks())
51 | .catch(error => console.log('error', error))
52 | }
--------------------------------------------------------------------------------
/books-js-client/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Books
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | Title |
17 | Author |
18 | Isbn |
19 | Action |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/books-js-client/styles.css:
--------------------------------------------------------------------------------
1 | *{
2 | font-family: 'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif;
3 | font-size: 20px;
4 | }
5 |
6 | table{
7 | border:1px solid #234465;
8 | padding: 1%;
9 | margin: 0 auto;
10 | width: 70%;
11 | border-radius: 4px;
12 | }
13 | table th{
14 | text-decoration: underline;
15 | text-align: center;
16 | }
17 | table tbody td{
18 | text-align: center;
19 | border-bottom:1px solid black;
20 | padding: 0.5%;
21 | }
22 |
23 | table tbody tr:nth-child(even){
24 | background-color: #234465;
25 | color: white
26 | }
27 |
28 | table tbody tr:nth-child(odd){
29 | color: #234465;
30 | border-bottom: 1px solid black;
31 | }
32 |
33 | table tbody tr:nth-child(odd) button{
34 | background: #234465;
35 | color:white;
36 | }
37 |
38 | table tbody tr:nth-child(even) button{
39 | background: white;
40 | color:#234465;
41 | }
42 |
43 | table tr td:not(:last-child):hover{
44 | text-decoration: underline;
45 | }
46 |
47 | table button{
48 | border: none;
49 | padding: 5px;
50 | border-radius: 5px;
51 | margin-left: 8%;
52 | }
53 |
54 | table button:hover{
55 | text-decoration: underline;
56 | }
57 |
58 | button#reloadBooks{
59 | margin: 0 auto;
60 | display:block;
61 | padding:1%;
62 | margin-top: 1%;
63 | margin-bottom: 1%;
64 | border-radius: 4px;
65 | }
66 |
67 |
68 | label, input{
69 | display:block;
70 | margin: 0 auto;
71 | }
72 |
73 | form input{
74 | margin-bottom: 1%;
75 | width: 25%;
76 | }
77 |
78 | form{
79 | display: block;
80 | margin: 0 auto;
81 | text-align: center;
82 | }
83 |
84 | form button{
85 | margin: 0 auto;
86 | display:block;
87 | margin-top: 1%;
88 | margin-bottom: 1%;
89 | border-radius: 4px;
90 | background-color: #234465;
91 | color:white;
92 | border: none;
93 | padding: 0.5%;
94 | }
--------------------------------------------------------------------------------
/books-server/.gitignore:
--------------------------------------------------------------------------------
1 | HELP.md
2 | .gradle
3 | build/
4 | !gradle/wrapper/gradle-wrapper.jar
5 | !**/src/main/**/build/
6 | !**/src/test/**/build/
7 |
8 | ### STS ###
9 | .apt_generated
10 | .classpath
11 | .factorypath
12 | .project
13 | .settings
14 | .springBeans
15 | .sts4-cache
16 | bin/
17 | !**/src/main/**/bin/
18 | !**/src/test/**/bin/
19 |
20 | ### IntelliJ IDEA ###
21 | .idea
22 | *.iws
23 | *.iml
24 | *.ipr
25 | out/
26 | !**/src/main/**/out/
27 | !**/src/test/**/out/
28 |
29 | ### NetBeans ###
30 | /nbproject/private/
31 | /nbbuild/
32 | /dist/
33 | /nbdist/
34 | /.nb-gradle/
35 |
36 | ### VS Code ###
37 | .vscode/
38 |
--------------------------------------------------------------------------------
/books-server/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'java'
3 | id 'org.springframework.boot' version '3.1.5'
4 | id 'io.spring.dependency-management' version '1.1.3'
5 | }
6 |
7 | group = 'bg.softuni'
8 | version = '0.0.1-SNAPSHOT'
9 |
10 | java {
11 | sourceCompatibility = '17'
12 | }
13 |
14 | repositories {
15 | mavenCentral()
16 | }
17 |
18 | dependencies {
19 | implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
20 | implementation 'org.springframework.boot:spring-boot-starter-web'
21 | runtimeOnly 'com.mysql:mysql-connector-j'
22 | testImplementation 'org.springframework.boot:spring-boot-starter-test'
23 | }
24 |
25 | tasks.named('test') {
26 | useJUnitPlatform()
27 | }
28 |
--------------------------------------------------------------------------------
/books-server/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luchob/softuni-sep-2023/c640aaad1a3e34eba81af8ba5ed433ea3899f529/books-server/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/books-server/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/books-server/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'books-server'
2 |
--------------------------------------------------------------------------------
/books-server/src/main/java/bg/softuni/booksserver/BooksServerApplication.java:
--------------------------------------------------------------------------------
1 | package bg.softuni.booksserver;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | @SpringBootApplication
7 | public class BooksServerApplication {
8 |
9 | public static void main(String[] args) {
10 | SpringApplication.run(BooksServerApplication.class, args);
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/books-server/src/main/java/bg/softuni/booksserver/init/DBInit.java:
--------------------------------------------------------------------------------
1 | package bg.softuni.booksserver.init;
2 |
3 | import bg.softuni.booksserver.model.entity.AuthorEntity;
4 | import bg.softuni.booksserver.model.entity.BookEntity;
5 | import bg.softuni.booksserver.repository.AuthorRepository;
6 | import bg.softuni.booksserver.repository.BookRepository;
7 | import java.util.ArrayList;
8 | import java.util.List;
9 | import java.util.UUID;
10 | import org.springframework.boot.CommandLineRunner;
11 | import org.springframework.stereotype.Component;
12 |
13 | @Component
14 | public class DBInit implements CommandLineRunner {
15 |
16 | private final AuthorRepository authorRepository;
17 | private final BookRepository bookRepository;
18 |
19 |
20 | DBInit(
21 | AuthorRepository authorRepository,
22 | BookRepository bookRepository) {
23 |
24 | this.authorRepository = authorRepository;
25 | this.bookRepository = bookRepository;
26 | }
27 |
28 | @Override
29 | public void run(String... args) throws Exception {
30 |
31 | if (bookRepository.count() == 0 && authorRepository.count() == 0) {
32 | initJovkov();
33 | initNikolaiHaitov();
34 | initDimitarTalev();
35 | initElinPelin();
36 | initVazov();
37 | }
38 | }
39 |
40 | private void initNikolaiHaitov() {
41 | initAuthor("Николай Хайтов",
42 | "Диви Разкази"
43 | );
44 | }
45 |
46 | private void initDimitarTalev() {
47 | initAuthor("Димитър Димов",
48 | "Тютюн"
49 | );
50 | }
51 |
52 | private void initElinPelin() {
53 | initAuthor("Елин Пелин",
54 | "Пижо и Пендо",
55 | "Ян Бибиян на луната",
56 | "Под манастирската лоза"
57 | );
58 | }
59 |
60 | private void initVazov() {
61 | initAuthor("Иван Вазов",
62 | "Пряпорец и Гусла",
63 | "Под Игото",
64 | "Тъгите на България"
65 | );
66 | }
67 |
68 | private void initJovkov() {
69 |
70 | initAuthor("Йордан Йовков",
71 | "Старопланински легенди",
72 | "Чифликът край границата");
73 | }
74 |
75 | private void initAuthor(String authorName, String... books) {
76 | AuthorEntity author = new AuthorEntity();
77 | author.setName(authorName);
78 | author = authorRepository.save(author);
79 |
80 | List allBooks = new ArrayList<>();
81 |
82 | for (String book: books) {
83 | BookEntity aBook = new BookEntity();
84 | aBook.setAuthor(author);
85 | aBook.setTitle(book);
86 | aBook.setIsbn(UUID.randomUUID().toString());//random string, not real, dummy
87 | allBooks.add(aBook);
88 | }
89 |
90 | author.setBooks(allBooks);
91 | authorRepository.save(author);
92 |
93 | bookRepository.saveAll(allBooks);
94 | }
95 | }
--------------------------------------------------------------------------------
/books-server/src/main/java/bg/softuni/booksserver/model/dto/AuthorDTO.java:
--------------------------------------------------------------------------------
1 | package bg.softuni.booksserver.model.dto;
2 |
3 | public class AuthorDTO {
4 | private String name;
5 |
6 | public String getName() {
7 | return name;
8 | }
9 |
10 | public AuthorDTO setName(String name) {
11 | this.name = name;
12 | return this;
13 | }
14 |
15 | @Override
16 | public String toString() {
17 | return "AuthorDTO{" +
18 | "name='" + name + '\'' +
19 | '}';
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/books-server/src/main/java/bg/softuni/booksserver/model/dto/BookDTO.java:
--------------------------------------------------------------------------------
1 | package bg.softuni.booksserver.model.dto;
2 |
3 | public class BookDTO {
4 |
5 | private Long id;
6 | private String title;
7 | private String isbn;
8 | private AuthorDTO author;
9 |
10 | public Long getId() {
11 | return id;
12 | }
13 |
14 | public BookDTO setId(Long id) {
15 | this.id = id;
16 | return this;
17 | }
18 |
19 | public String getTitle() {
20 | return title;
21 | }
22 |
23 | public BookDTO setTitle(String title) {
24 | this.title = title;
25 | return this;
26 | }
27 |
28 | public String getIsbn() {
29 | return isbn;
30 | }
31 |
32 | public BookDTO setIsbn(String isbn) {
33 | this.isbn = isbn;
34 | return this;
35 | }
36 |
37 | public AuthorDTO getAuthor() {
38 | return author;
39 | }
40 |
41 | public BookDTO setAuthor(AuthorDTO author) {
42 | this.author = author;
43 | return this;
44 | }
45 |
46 | @Override
47 | public String toString() {
48 | return "BookDTO{" +
49 | "id=" + id +
50 | ", title='" + title + '\'' +
51 | ", isbn='" + isbn + '\'' +
52 | ", author=" + author +
53 | '}';
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/books-server/src/main/java/bg/softuni/booksserver/model/entity/AuthorEntity.java:
--------------------------------------------------------------------------------
1 | package bg.softuni.booksserver.model.entity;
2 |
3 | import jakarta.persistence.Entity;
4 | import jakarta.persistence.GeneratedValue;
5 | import jakarta.persistence.GenerationType;
6 | import jakarta.persistence.Id;
7 | import jakarta.persistence.OneToMany;
8 | import jakarta.persistence.Table;
9 | import java.util.List;
10 |
11 | @Entity
12 | @Table(name = "authors")
13 | public class AuthorEntity {
14 |
15 | @Id
16 | @GeneratedValue(strategy = GenerationType.IDENTITY)
17 | private Long id;
18 | private String name;
19 |
20 | @OneToMany(mappedBy = "author")
21 | private List books;
22 |
23 | public Long getId() {
24 | return id;
25 | }
26 |
27 | public AuthorEntity setId(Long id) {
28 | this.id = id;
29 | return this;
30 | }
31 |
32 | public String getName() {
33 | return name;
34 | }
35 |
36 | public AuthorEntity setName(String name) {
37 | this.name = name;
38 | return this;
39 | }
40 |
41 | public List getBooks() {
42 | return books;
43 | }
44 |
45 | public AuthorEntity setBooks(List books) {
46 | this.books = books;
47 | return this;
48 | }
49 |
50 | @Override
51 | public String toString() {
52 | return "AuthorEntity{" +
53 | "id=" + id +
54 | ", name='" + name + '\'' +
55 | ", books=" + books +
56 | '}';
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/books-server/src/main/java/bg/softuni/booksserver/model/entity/BookEntity.java:
--------------------------------------------------------------------------------
1 | package bg.softuni.booksserver.model.entity;
2 |
3 | import jakarta.persistence.Entity;
4 | import jakarta.persistence.GeneratedValue;
5 | import jakarta.persistence.GenerationType;
6 | import jakarta.persistence.Id;
7 | import jakarta.persistence.ManyToOne;
8 | import jakarta.persistence.Table;
9 |
10 | @Entity
11 | @Table(name = "books")
12 | public class BookEntity {
13 |
14 | @Id
15 | @GeneratedValue(strategy = GenerationType.IDENTITY)
16 | private Long id;
17 | private String title;
18 | private String isbn;
19 |
20 | @ManyToOne
21 | private AuthorEntity author;
22 |
23 | public Long getId() {
24 | return id;
25 | }
26 |
27 | public BookEntity setId(Long id) {
28 | this.id = id;
29 | return this;
30 | }
31 |
32 | public String getTitle() {
33 | return title;
34 | }
35 |
36 | public BookEntity setTitle(String title) {
37 | this.title = title;
38 | return this;
39 | }
40 |
41 | public String getIsbn() {
42 | return isbn;
43 | }
44 |
45 | public BookEntity setIsbn(String isbn) {
46 | this.isbn = isbn;
47 | return this;
48 | }
49 |
50 | public AuthorEntity getAuthor() {
51 | return author;
52 | }
53 |
54 | public BookEntity setAuthor(AuthorEntity author) {
55 | this.author = author;
56 | return this;
57 | }
58 |
59 | @Override
60 | public String toString() {
61 | return "BookEntity{" +
62 | "id=" + id +
63 | ", title='" + title + '\'' +
64 | ", isbn='" + isbn + '\'' +
65 | ", author=" + (author != null ? author.getName() : null) +
66 | '}';
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/books-server/src/main/java/bg/softuni/booksserver/repository/AuthorRepository.java:
--------------------------------------------------------------------------------
1 | package bg.softuni.booksserver.repository;
2 |
3 |
4 | import bg.softuni.booksserver.model.entity.AuthorEntity;
5 | import java.util.Optional;
6 | import org.springframework.data.jpa.repository.JpaRepository;
7 | import org.springframework.stereotype.Repository;
8 | @Repository
9 | public interface AuthorRepository extends JpaRepository {
10 | Optional findByName(String name);
11 | }
--------------------------------------------------------------------------------
/books-server/src/main/java/bg/softuni/booksserver/repository/BookRepository.java:
--------------------------------------------------------------------------------
1 | package bg.softuni.booksserver.repository;
2 |
3 | import bg.softuni.booksserver.model.entity.BookEntity;
4 | import org.springframework.data.jpa.repository.JpaRepository;
5 | import org.springframework.stereotype.Repository;
6 |
7 | @Repository
8 | public interface BookRepository extends JpaRepository {
9 |
10 | }
11 |
--------------------------------------------------------------------------------
/books-server/src/main/java/bg/softuni/booksserver/service/BookService.java:
--------------------------------------------------------------------------------
1 | package bg.softuni.booksserver.service;
2 |
3 | import bg.softuni.booksserver.model.dto.BookDTO;
4 | import java.util.List;
5 | import java.util.Optional;
6 |
7 | public interface BookService {
8 |
9 | List getAllBooks();
10 |
11 | Optional findBookById(Long id);
12 |
13 | void deleteBookByID(Long id);
14 |
15 | Long createBook(BookDTO bookDTO);
16 | }
17 |
--------------------------------------------------------------------------------
/books-server/src/main/java/bg/softuni/booksserver/service/impl/BookServiceImpl.java:
--------------------------------------------------------------------------------
1 | package bg.softuni.booksserver.service.impl;
2 |
3 | import bg.softuni.booksserver.model.dto.AuthorDTO;
4 | import bg.softuni.booksserver.model.dto.BookDTO;
5 | import bg.softuni.booksserver.model.entity.AuthorEntity;
6 | import bg.softuni.booksserver.model.entity.BookEntity;
7 | import bg.softuni.booksserver.repository.AuthorRepository;
8 | import bg.softuni.booksserver.repository.BookRepository;
9 | import bg.softuni.booksserver.service.BookService;
10 | import java.util.List;
11 | import java.util.Optional;
12 | import org.springframework.stereotype.Service;
13 |
14 | @Service
15 | public class BookServiceImpl implements BookService {
16 |
17 | private final BookRepository bookRepository;
18 | private final AuthorRepository authorRepository;
19 |
20 | public BookServiceImpl(BookRepository bookRepository,
21 | AuthorRepository authorRepository) {
22 | this.bookRepository = bookRepository;
23 | this.authorRepository = authorRepository;
24 | }
25 |
26 | @Override
27 | public List getAllBooks() {
28 | return bookRepository
29 | .findAll()
30 | .stream()
31 | .map(BookServiceImpl::mapBookToDTO)
32 | .toList();
33 | }
34 |
35 | @Override
36 | public Optional findBookById(Long id) {
37 | return bookRepository
38 | .findById(id)
39 | .map(BookServiceImpl::mapBookToDTO);
40 | }
41 |
42 | @Override
43 | public void deleteBookByID(Long id) {
44 | this.bookRepository.deleteById(id);
45 | }
46 |
47 | @Override
48 | public Long createBook(BookDTO bookDTO) {
49 |
50 | Optional authorOpt = authorRepository
51 | .findByName(bookDTO.getAuthor().getName());
52 |
53 | BookEntity newBook = new BookEntity()
54 | .setAuthor(authorOpt.orElseGet(() ->
55 | authorRepository.save(new AuthorEntity()
56 | .setName(bookDTO.getAuthor().getName()))))
57 | .setIsbn(bookDTO.getIsbn())
58 | .setTitle(bookDTO.getTitle());
59 |
60 | newBook = bookRepository.save(newBook);
61 |
62 | return newBook.getId();
63 | }
64 |
65 | private static BookDTO mapBookToDTO(BookEntity bookEntity) {
66 |
67 | AuthorDTO authorDTO = new AuthorDTO()
68 | .setName(bookEntity.getAuthor().getName());
69 |
70 | return new BookDTO()
71 | .setId(bookEntity.getId())
72 | .setAuthor(authorDTO)
73 | .setTitle(bookEntity.getTitle())
74 | .setIsbn(bookEntity.getIsbn());
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/books-server/src/main/java/bg/softuni/booksserver/web/BooksRestController.java:
--------------------------------------------------------------------------------
1 | package bg.softuni.booksserver.web;
2 |
3 | import bg.softuni.booksserver.model.dto.BookDTO;
4 | import bg.softuni.booksserver.service.BookService;
5 | import java.util.List;
6 | import java.util.Optional;
7 | import org.springframework.http.ResponseEntity;
8 | import org.springframework.web.bind.annotation.CrossOrigin;
9 | import org.springframework.web.bind.annotation.DeleteMapping;
10 | import org.springframework.web.bind.annotation.GetMapping;
11 | import org.springframework.web.bind.annotation.PathVariable;
12 | import org.springframework.web.bind.annotation.PostMapping;
13 | import org.springframework.web.bind.annotation.RequestBody;
14 | import org.springframework.web.bind.annotation.RequestMapping;
15 | import org.springframework.web.bind.annotation.RestController;
16 | import org.springframework.web.util.UriComponentsBuilder;
17 |
18 | @CrossOrigin("*")
19 | @RestController
20 | @RequestMapping("/api/books")
21 | public class BooksRestController {
22 |
23 | private final BookService bookService;
24 |
25 | public BooksRestController(BookService bookService) {
26 | this.bookService = bookService;
27 | }
28 |
29 | @GetMapping
30 | public ResponseEntity> getAllBooks() {
31 | return ResponseEntity.ok(bookService.getAllBooks());
32 | }
33 |
34 | @GetMapping("/{id}")
35 | public ResponseEntity findBookById(@PathVariable("id") Long id) {
36 | Optional bookDTOOptional = bookService.findBookById(id);
37 |
38 | return bookDTOOptional
39 | .map(ResponseEntity::ok)
40 | .orElse(ResponseEntity.notFound().build());
41 | }
42 |
43 | @DeleteMapping("/{id}")
44 | public ResponseEntity deleteBookByID(@PathVariable("id") Long id) {
45 |
46 | bookService.deleteBookByID(id);
47 |
48 | return ResponseEntity
49 | .noContent()
50 | .build();
51 | }
52 |
53 | @PostMapping
54 | public ResponseEntity createBook(
55 | @RequestBody BookDTO bookDTO,
56 | UriComponentsBuilder uriComponentsBuilder) {
57 |
58 | long newBookID = bookService.createBook(bookDTO);
59 |
60 | return ResponseEntity.created(
61 | uriComponentsBuilder.path("/api/books/{id}").build(newBookID)
62 | ).build();
63 | }
64 |
65 | }
66 |
--------------------------------------------------------------------------------
/books-server/src/main/resources/application.yaml:
--------------------------------------------------------------------------------
1 | spring:
2 | datasource:
3 | driverClassName: com.mysql.cj.jdbc.Driver
4 | url: jdbc:mysql://${MYSQL_HOST:localhost}:3306/books?allowPublicKeyRetrieval=true&useSSL=false&createDatabaseIfNotExist=true&serverTimezone=UTC
5 | username: ${MYSQL_USER:root}
6 | password: ${MYSQL_PASSWORD:}
7 | jpa:
8 | database-platform: org.hibernate.dialect.MySQLDialect
9 | defer-datasource-initialization: true
10 | properties:
11 | hibernate:
12 | format_sql: true
13 | hibernate:
14 | ddl-auto: update
15 |
16 | logging:
17 | level:
18 | org.hibernate.SQL: DEBUG
19 | org.hibernate.orm.jdbc.bind: TRACE
20 |
21 |
--------------------------------------------------------------------------------
/books-server/src/test/java/bg/softuni/booksserver/BooksServerApplicationTests.java:
--------------------------------------------------------------------------------
1 | package bg.softuni.booksserver;
2 |
3 | import org.junit.jupiter.api.Test;
4 | import org.springframework.boot.test.context.SpringBootTest;
5 |
6 | @SpringBootTest
7 | class BooksServerApplicationTests {
8 |
9 | @Test
10 | void contextLoads() {
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/boot-demo/.gitignore:
--------------------------------------------------------------------------------
1 | HELP.md
2 | .gradle
3 | build/
4 | !gradle/wrapper/gradle-wrapper.jar
5 | !**/src/main/**/build/
6 | !**/src/test/**/build/
7 |
8 | ### STS ###
9 | .apt_generated
10 | .classpath
11 | .factorypath
12 | .project
13 | .settings
14 | .springBeans
15 | .sts4-cache
16 | bin/
17 | !**/src/main/**/bin/
18 | !**/src/test/**/bin/
19 |
20 | ### IntelliJ IDEA ###
21 | .idea
22 | *.iws
23 | *.iml
24 | *.ipr
25 | out/
26 | !**/src/main/**/out/
27 | !**/src/test/**/out/
28 |
29 | ### NetBeans ###
30 | /nbproject/private/
31 | /nbbuild/
32 | /dist/
33 | /nbdist/
34 | /.nb-gradle/
35 |
36 | ### VS Code ###
37 | .vscode/
38 |
--------------------------------------------------------------------------------
/boot-demo/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'java'
3 | id 'org.springframework.boot' version '3.1.3'
4 | id 'io.spring.dependency-management' version '1.1.3'
5 | }
6 |
7 | group = 'org.softuni'
8 | version = '0.0.1-SNAPSHOT'
9 |
10 | java {
11 | sourceCompatibility = '17'
12 | }
13 |
14 | repositories {
15 | mavenCentral()
16 | }
17 |
18 | dependencies {
19 | implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
20 | implementation 'org.springframework.boot:spring-boot-starter-web'
21 | implementation 'com.google.code.gson:gson'
22 | testImplementation 'org.springframework.boot:spring-boot-starter-test'
23 | }
24 |
25 | tasks.named('test') {
26 | useJUnitPlatform()
27 | }
28 |
--------------------------------------------------------------------------------
/boot-demo/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luchob/softuni-sep-2023/c640aaad1a3e34eba81af8ba5ed433ea3899f529/boot-demo/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/boot-demo/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-bin.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/boot-demo/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'boot-demo'
2 |
--------------------------------------------------------------------------------
/boot-demo/src/main/java/org/softuni/bootdemo/BootDemoApplication.java:
--------------------------------------------------------------------------------
1 | package org.softuni.bootdemo;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | @SpringBootApplication
7 | public class BootDemoApplication {
8 |
9 | public static void main(String[] args) {
10 | SpringApplication.run(BootDemoApplication.class, args);
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/boot-demo/src/main/java/org/softuni/bootdemo/util/CLGreeter.java:
--------------------------------------------------------------------------------
1 | package org.softuni.bootdemo.util;
2 |
3 | import com.google.gson.Gson;
4 | import org.springframework.boot.CommandLineRunner;
5 | import org.springframework.stereotype.Component;
6 | @Component
7 | public class CLGreeter implements CommandLineRunner {
8 |
9 | private final Gson gson;
10 |
11 | public CLGreeter(Gson gson) {
12 | this.gson = gson;
13 | }
14 |
15 | @Override
16 | public void run(String... args) throws Exception {
17 | var json = gson.toJson(new Point(1, 4));
18 | System.out.println("My point: " + json);
19 | }
20 |
21 | record Point(int x, int y) {
22 |
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/boot-demo/src/main/java/org/softuni/bootdemo/web/HelloController.java:
--------------------------------------------------------------------------------
1 | package org.softuni.bootdemo.web;
2 |
3 | import org.springframework.beans.factory.annotation.Value;
4 | import org.springframework.stereotype.Controller;
5 | import org.springframework.ui.Model;
6 | import org.springframework.web.bind.annotation.GetMapping;
7 |
8 | @Controller
9 | public class HelloController {
10 |
11 |
12 | private final String greeting;
13 |
14 | public HelloController(@Value("${demo.greeting.message}") String greeting) {
15 | this.greeting = greeting;
16 | }
17 |
18 | @GetMapping("/test")
19 | public String test(Model model) {
20 | model.addAttribute("greeting", greeting);
21 | return "test";
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/boot-demo/src/main/resources/application.yaml:
--------------------------------------------------------------------------------
1 | demo:
2 | greeting:
3 | message: Hello, softuni
4 |
--------------------------------------------------------------------------------
/boot-demo/src/main/resources/templates/test.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
7 | Hello, world!
8 |
9 |
10 |
--------------------------------------------------------------------------------
/boot-demo/src/test/java/org/softuni/bootdemo/BootDemoApplicationTests.java:
--------------------------------------------------------------------------------
1 | package org.softuni.bootdemo;
2 |
3 | import org.junit.jupiter.api.Test;
4 | import org.springframework.boot.test.context.SpringBootTest;
5 |
6 | @SpringBootTest
7 | class BootDemoApplicationTests {
8 |
9 | @Test
10 | void contextLoads() {
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/cache-redis/.gitignore:
--------------------------------------------------------------------------------
1 | HELP.md
2 | .gradle
3 | build/
4 | !gradle/wrapper/gradle-wrapper.jar
5 | !**/src/main/**/build/
6 | !**/src/test/**/build/
7 |
8 | ### STS ###
9 | .apt_generated
10 | .classpath
11 | .factorypath
12 | .project
13 | .settings
14 | .springBeans
15 | .sts4-cache
16 | bin/
17 | !**/src/main/**/bin/
18 | !**/src/test/**/bin/
19 |
20 | ### IntelliJ IDEA ###
21 | .idea
22 | *.iws
23 | *.iml
24 | *.ipr
25 | out/
26 | !**/src/main/**/out/
27 | !**/src/test/**/out/
28 |
29 | ### NetBeans ###
30 | /nbproject/private/
31 | /nbbuild/
32 | /dist/
33 | /nbdist/
34 | /.nb-gradle/
35 |
36 | ### VS Code ###
37 | .vscode/
38 |
--------------------------------------------------------------------------------
/cache-redis/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'java'
3 | id 'org.springframework.boot' version '3.1.5'
4 | id 'io.spring.dependency-management' version '1.1.3'
5 | }
6 |
7 | group = 'bg.softuni.cache'
8 | version = '0.0.1-SNAPSHOT'
9 |
10 | java {
11 | sourceCompatibility = '17'
12 | }
13 |
14 | repositories {
15 | mavenCentral()
16 | }
17 |
18 | dependencies {
19 | implementation 'org.springframework.boot:spring-boot-starter-cache'
20 | implementation 'org.springframework.boot:spring-boot-starter-data-redis'
21 | implementation 'org.springframework.boot:spring-boot-starter-web'
22 | testImplementation 'org.springframework.boot:spring-boot-starter-test'
23 | }
24 |
25 | tasks.named('test') {
26 | useJUnitPlatform()
27 | }
28 |
--------------------------------------------------------------------------------
/cache-redis/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luchob/softuni-sep-2023/c640aaad1a3e34eba81af8ba5ed433ea3899f529/cache-redis/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/cache-redis/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/cache-redis/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'cache-redis'
2 |
--------------------------------------------------------------------------------
/cache-redis/src/main/java/bg/softuni/cache/redis/CacheRedisApplication.java:
--------------------------------------------------------------------------------
1 | package bg.softuni.cache.redis;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 | import org.springframework.cache.annotation.EnableCaching;
6 |
7 | @SpringBootApplication
8 | @EnableCaching
9 | public class CacheRedisApplication {
10 |
11 | public static void main(String[] args) {
12 | SpringApplication.run(CacheRedisApplication.class, args);
13 | }
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/cache-redis/src/main/java/bg/softuni/cache/redis/config/RedisConfiguration.java:
--------------------------------------------------------------------------------
1 | package bg.softuni.cache.redis.config;
2 |
3 | import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
4 | import com.fasterxml.jackson.databind.ObjectMapper;
5 | import com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping;
6 | import java.time.Duration;
7 | import org.springframework.context.annotation.Bean;
8 | import org.springframework.context.annotation.Configuration;
9 | import org.springframework.data.redis.cache.RedisCacheConfiguration;
10 | import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
11 | import org.springframework.data.redis.serializer.RedisSerializationContext.SerializationPair;
12 | import org.springframework.data.redis.serializer.StringRedisSerializer;
13 |
14 | @Configuration
15 | public class RedisConfiguration {
16 |
17 | @Bean
18 | public RedisCacheConfiguration cacheConfiguration(ObjectMapper objectMapper) {
19 |
20 | objectMapper = objectMapper.copy();
21 | objectMapper.activateDefaultTyping(
22 | objectMapper.getPolymorphicTypeValidator(),
23 | DefaultTyping.NON_FINAL,
24 | As.PROPERTY
25 | );
26 |
27 | return RedisCacheConfiguration.defaultCacheConfig()
28 | .entryTtl(Duration.ofMinutes(5))
29 | .serializeKeysWith(SerializationPair.fromSerializer(new StringRedisSerializer()))
30 | .serializeValuesWith(SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer(objectMapper)));
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/cache-redis/src/main/java/bg/softuni/cache/redis/inti/Init.java:
--------------------------------------------------------------------------------
1 | package bg.softuni.cache.redis.inti;
2 |
3 | import bg.softuni.cache.redis.service.StudentService;
4 | import org.springframework.boot.CommandLineRunner;
5 | import org.springframework.stereotype.Component;
6 |
7 | @Component
8 | public class Init implements CommandLineRunner {
9 |
10 | private final StudentService studentService;
11 |
12 | public Init(StudentService studentService) {
13 | this.studentService = studentService;
14 | }
15 |
16 | @Override
17 | public void run(String... args) throws Exception {
18 | studentService.getAllStudents();
19 | studentService.getAllStudents();
20 | studentService.getAllStudents();
21 | studentService.getAllStudents().forEach(
22 | System.out::println
23 | );
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/cache-redis/src/main/java/bg/softuni/cache/redis/model/StudentDTO.java:
--------------------------------------------------------------------------------
1 | package bg.softuni.cache.redis.model;
2 |
3 | public record StudentDTO(String name, int age) {
4 | }
5 |
--------------------------------------------------------------------------------
/cache-redis/src/main/java/bg/softuni/cache/redis/service/StudentService.java:
--------------------------------------------------------------------------------
1 | package bg.softuni.cache.redis.service;
2 |
3 | import bg.softuni.cache.redis.model.StudentDTO;
4 | import java.util.ArrayList;
5 | import java.util.List;
6 | import org.springframework.cache.annotation.Cacheable;
7 | import org.springframework.stereotype.Service;
8 |
9 | @Service
10 | public class StudentService {
11 |
12 | @Cacheable("students")
13 | public List getAllStudents() {
14 |
15 | System.out.println("Calculating students...");
16 |
17 | List allStudents = new ArrayList<>();
18 | allStudents.add(new StudentDTO("Pesho", 40));
19 | allStudents.add(new StudentDTO("Anna", 39));
20 |
21 | return allStudents;
22 | }
23 |
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/cache-redis/src/main/resources/application.yaml:
--------------------------------------------------------------------------------
1 | spring:
2 | data:
3 | redis:
4 | host: localhost
5 | port: 6379
6 |
--------------------------------------------------------------------------------
/cache-redis/src/test/java/bg/softuni/cache/redis/CacheRedisApplicationTests.java:
--------------------------------------------------------------------------------
1 | package bg.softuni.cache.redis;
2 |
3 | import org.junit.jupiter.api.Test;
4 | import org.springframework.boot.test.context.SpringBootTest;
5 |
6 | @SpringBootTest
7 | class CacheRedisApplicationTests {
8 |
9 | @Test
10 | void contextLoads() {
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/dep-inj/.gitignore:
--------------------------------------------------------------------------------
1 | .gradle
2 | build/
3 | !gradle/wrapper/gradle-wrapper.jar
4 | !**/src/main/**/build/
5 | !**/src/test/**/build/
6 |
7 | ### IntelliJ IDEA ###
8 | .idea
9 | .idea/modules.xml
10 | .idea/jarRepositories.xml
11 | .idea/compiler.xml
12 | .idea/libraries/
13 | *.iws
14 | *.iml
15 | *.ipr
16 | out/
17 | !**/src/main/**/out/
18 | !**/src/test/**/out/
19 |
20 | ### Eclipse ###
21 | .apt_generated
22 | .classpath
23 | .factorypath
24 | .project
25 | .settings
26 | .springBeans
27 | .sts4-cache
28 | bin/
29 | !**/src/main/**/bin/
30 | !**/src/test/**/bin/
31 |
32 | ### NetBeans ###
33 | /nbproject/private/
34 | /nbbuild/
35 | /dist/
36 | /nbdist/
37 | /.nb-gradle/
38 |
39 | ### VS Code ###
40 | .vscode/
41 |
42 | ### Mac OS ###
43 | .DS_Store
--------------------------------------------------------------------------------
/dep-inj/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'java'
3 | }
4 |
5 | group = 'org.example'
6 | version = '1.0-SNAPSHOT'
7 |
8 | repositories {
9 | mavenCentral()
10 | }
11 |
12 | dependencies {
13 |
14 | implementation("org.springframework:spring-context:6.0.11")
15 |
16 | testImplementation platform('org.junit:junit-bom:5.9.1')
17 | testImplementation 'org.junit.jupiter:junit-jupiter'
18 | }
19 |
20 | test {
21 | useJUnitPlatform()
22 | }
--------------------------------------------------------------------------------
/dep-inj/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luchob/softuni-sep-2023/c640aaad1a3e34eba81af8ba5ed433ea3899f529/dep-inj/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/dep-inj/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Sep 18 18:43:33 EEST 2023
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
5 | zipStoreBase=GRADLE_USER_HOME
6 | zipStorePath=wrapper/dists
7 |
--------------------------------------------------------------------------------
/dep-inj/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'dep-inj'
2 |
3 |
--------------------------------------------------------------------------------
/dep-inj/src/main/java/org/softuni/Main.java:
--------------------------------------------------------------------------------
1 | package org.softuni;
2 |
3 | import org.softuni.service.UserService;
4 | import org.springframework.context.ApplicationContext;
5 | import org.springframework.context.annotation.AnnotationConfigApplicationContext;
6 |
7 | public class Main {
8 |
9 | public static void main(String[] args) {
10 | ApplicationContext context = new AnnotationConfigApplicationContext("org.softuni");
11 |
12 | UserService userService1 = context.getBean(UserService.class);
13 | UserService userService2 = context.getBean(UserService.class);
14 |
15 | System.out.println(userService1 == userService2);
16 | }
17 | }
--------------------------------------------------------------------------------
/dep-inj/src/main/java/org/softuni/model/User.java:
--------------------------------------------------------------------------------
1 | package org.softuni.model;
2 |
3 | public record User(
4 | String firstName,
5 | String lastName,
6 | int age) {
7 |
8 | public String description() {
9 | return firstName() + " " + lastName() + ", Age: " + age;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/dep-inj/src/main/java/org/softuni/repository/FileBasedUserRepository.java:
--------------------------------------------------------------------------------
1 | package org.softuni.repository;
2 |
3 | import java.io.BufferedInputStream;
4 | import java.io.BufferedReader;
5 | import java.io.InputStreamReader;
6 | import java.util.List;
7 | import org.softuni.model.User;
8 | import org.springframework.stereotype.Repository;
9 |
10 | @Repository("fileRepo")
11 | public class FileBasedUserRepository implements UserRepository {
12 |
13 | @Override
14 | public List findAll() {
15 | return new BufferedReader(
16 | new InputStreamReader(ClassLoader.getSystemResourceAsStream("users.csv")))
17 | .lines()
18 | .map(this::parseUser)
19 | .toList();
20 | }
21 |
22 | private User parseUser(String line) {
23 | String[] tokens = line.split(",");
24 | return new User(tokens[0], tokens[1], Integer.parseInt(tokens[2]));
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/dep-inj/src/main/java/org/softuni/repository/InMemoryUserRepository.java:
--------------------------------------------------------------------------------
1 | package org.softuni.repository;
2 |
3 | import java.util.List;
4 | import org.softuni.model.User;
5 | import org.springframework.stereotype.Repository;
6 |
7 | @Repository("memoryRepo")
8 | public class InMemoryUserRepository implements UserRepository {
9 | @Override
10 | public List findAll() {
11 | return List.of(
12 | new User("Ivan", "Ivanov", 20),
13 | new User("Georgi", "Georgiev", 30),
14 | new User("Pesho", "Petrov", 40)
15 | );
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/dep-inj/src/main/java/org/softuni/repository/UserRepository.java:
--------------------------------------------------------------------------------
1 | package org.softuni.repository;
2 |
3 | import java.util.List;
4 | import org.softuni.model.User;
5 |
6 | public interface UserRepository {
7 | List findAll();
8 |
9 | }
10 |
--------------------------------------------------------------------------------
/dep-inj/src/main/java/org/softuni/service/UserService.java:
--------------------------------------------------------------------------------
1 | package org.softuni.service;
2 |
3 | import java.util.Optional;
4 | import org.softuni.model.User;
5 |
6 | public interface UserService {
7 | Optional findYoungestUser();
8 | }
9 |
--------------------------------------------------------------------------------
/dep-inj/src/main/java/org/softuni/service/UserServiceImpl.java:
--------------------------------------------------------------------------------
1 | package org.softuni.service;
2 |
3 | import java.util.Comparator;
4 | import java.util.Optional;
5 | import org.softuni.model.User;
6 | import org.softuni.repository.InMemoryUserRepository;
7 | import org.softuni.repository.UserRepository;
8 | import org.springframework.beans.factory.BeanNameAware;
9 | import org.springframework.beans.factory.annotation.Lookup;
10 | import org.springframework.beans.factory.annotation.Qualifier;
11 | import org.springframework.context.annotation.Scope;
12 | import org.springframework.stereotype.Component;
13 | import org.springframework.stereotype.Service;
14 |
15 | @Service
16 | public class UserServiceImpl implements UserService,
17 | BeanNameAware {
18 | private final UserRepository userRepository;
19 |
20 | public UserServiceImpl(
21 | @Qualifier("fileRepo")
22 | UserRepository userRepository) {
23 | this.userRepository = userRepository;
24 | }
25 |
26 | @Override
27 | public Optional findYoungestUser() {
28 | return userRepository.findAll().stream()
29 | .min(Comparator.comparingInt(User::age));
30 | }
31 |
32 | @Override
33 | public void setBeanName(String name) {
34 | System.out.println("My bean is named " + name);
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/dep-inj/src/main/resources/users.csv:
--------------------------------------------------------------------------------
1 | Anna,Petrova,20
2 | Nina,Bozhinova,45
--------------------------------------------------------------------------------
/errors/.gitignore:
--------------------------------------------------------------------------------
1 | HELP.md
2 | .gradle
3 | build/
4 | !gradle/wrapper/gradle-wrapper.jar
5 | !**/src/main/**/build/
6 | !**/src/test/**/build/
7 |
8 | ### STS ###
9 | .apt_generated
10 | .classpath
11 | .factorypath
12 | .project
13 | .settings
14 | .springBeans
15 | .sts4-cache
16 | bin/
17 | !**/src/main/**/bin/
18 | !**/src/test/**/bin/
19 |
20 | ### IntelliJ IDEA ###
21 | .idea
22 | *.iws
23 | *.iml
24 | *.ipr
25 | out/
26 | !**/src/main/**/out/
27 | !**/src/test/**/out/
28 |
29 | ### NetBeans ###
30 | /nbproject/private/
31 | /nbbuild/
32 | /dist/
33 | /nbdist/
34 | /.nb-gradle/
35 |
36 | ### VS Code ###
37 | .vscode/
38 |
--------------------------------------------------------------------------------
/errors/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'java'
3 | id 'org.springframework.boot' version '3.1.5'
4 | id 'io.spring.dependency-management' version '1.1.3'
5 | }
6 |
7 | group = 'bg.softuni'
8 | version = '0.0.1-SNAPSHOT'
9 |
10 | java {
11 | sourceCompatibility = '17'
12 | }
13 |
14 | repositories {
15 | mavenCentral()
16 | }
17 |
18 | dependencies {
19 | implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
20 | implementation 'org.springframework.boot:spring-boot-starter-web'
21 | testImplementation 'org.springframework.boot:spring-boot-starter-test'
22 | }
23 |
24 | tasks.named('test') {
25 | useJUnitPlatform()
26 | }
27 |
--------------------------------------------------------------------------------
/errors/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luchob/softuni-sep-2023/c640aaad1a3e34eba81af8ba5ed433ea3899f529/errors/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/errors/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/errors/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'errors'
2 |
--------------------------------------------------------------------------------
/errors/src/main/java/bg/softuni/errors/ErrorsApplication.java:
--------------------------------------------------------------------------------
1 | package bg.softuni.errors;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | @SpringBootApplication
7 | public class ErrorsApplication {
8 |
9 | public static void main(String[] args) {
10 | SpringApplication.run(ErrorsApplication.class, args);
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/errors/src/main/java/bg/softuni/errors/config/ErrorConfig.java:
--------------------------------------------------------------------------------
1 | package bg.softuni.errors.config;
2 |
3 | import java.util.Properties;
4 | import org.springframework.context.annotation.Bean;
5 | import org.springframework.context.annotation.Configuration;
6 | import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;
7 |
8 | @Configuration
9 | public class ErrorConfig {
10 |
11 | @Bean
12 | public SimpleMappingExceptionResolver simpleMappingExceptionResolver() {
13 |
14 | SimpleMappingExceptionResolver resolver = new SimpleMappingExceptionResolver();
15 |
16 | Properties properties = new Properties();
17 | properties.setProperty(IllegalArgumentException.class.getSimpleName(), "iae");
18 |
19 | resolver.setExceptionMappings(properties);
20 |
21 | return resolver;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/errors/src/main/java/bg/softuni/errors/exception/ObjectNotFoundException.java:
--------------------------------------------------------------------------------
1 | package bg.softuni.errors.exception;
2 |
3 | import org.springframework.http.HttpStatus;
4 | import org.springframework.web.bind.annotation.ResponseStatus;
5 |
6 | @ResponseStatus(code = HttpStatus.NOT_FOUND)
7 | public class ObjectNotFoundException extends RuntimeException{
8 |
9 | private final String id;
10 |
11 | public ObjectNotFoundException(String message, String id) {
12 | super(message);
13 | this.id = id;
14 | }
15 |
16 | public String getId() {
17 | return id;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/errors/src/main/java/bg/softuni/errors/exception/ObjectNotFoundRestException.java:
--------------------------------------------------------------------------------
1 | package bg.softuni.errors.exception;
2 |
3 | import org.springframework.http.HttpStatus;
4 | import org.springframework.web.bind.annotation.ResponseStatus;
5 |
6 | @ResponseStatus(HttpStatus.NOT_FOUND)
7 | public class ObjectNotFoundRestException extends RuntimeException{
8 |
9 | public ObjectNotFoundRestException(String message) {
10 | super(message);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/errors/src/main/java/bg/softuni/errors/web/GloabalNotFoundExceptionHandler.java:
--------------------------------------------------------------------------------
1 | package bg.softuni.errors.web;
2 |
3 | import bg.softuni.errors.exception.ObjectNotFoundException;
4 | import org.springframework.http.HttpStatus;
5 | import org.springframework.web.bind.annotation.ControllerAdvice;
6 | import org.springframework.web.bind.annotation.ExceptionHandler;
7 | import org.springframework.web.bind.annotation.ResponseStatus;
8 | import org.springframework.web.servlet.ModelAndView;
9 |
10 | @ControllerAdvice
11 | public class GloabalNotFoundExceptionHandler {
12 |
13 | @ResponseStatus(HttpStatus.NOT_FOUND)
14 | @ExceptionHandler(ObjectNotFoundException.class)
15 | public ModelAndView handleNotFound(ObjectNotFoundException exception) {
16 | ModelAndView modelAndView = new ModelAndView("notfound-global");
17 | modelAndView.addObject("id", exception.getId());
18 | return modelAndView;
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/errors/src/main/java/bg/softuni/errors/web/NotFound1Controller.java:
--------------------------------------------------------------------------------
1 | package bg.softuni.errors.web;
2 |
3 | import bg.softuni.errors.exception.ObjectNotFoundException;
4 | import org.springframework.http.HttpStatus;
5 | import org.springframework.stereotype.Controller;
6 | import org.springframework.web.bind.annotation.ExceptionHandler;
7 | import org.springframework.web.bind.annotation.GetMapping;
8 | import org.springframework.web.bind.annotation.PathVariable;
9 | import org.springframework.web.bind.annotation.ResponseStatus;
10 | import org.springframework.web.servlet.ModelAndView;
11 |
12 | @Controller
13 | public class NotFound1Controller {
14 |
15 | @GetMapping("/not-found1/{id}")
16 | public String notFound1(@PathVariable("id") String id) {
17 | throw new ObjectNotFoundException("Object with id " + id + " was not found!", id);
18 | }
19 |
20 | @ResponseStatus(HttpStatus.NOT_FOUND)
21 | @ExceptionHandler(ObjectNotFoundException.class)
22 | public ModelAndView handleNotFound(ObjectNotFoundException exception) {
23 | ModelAndView modelAndView = new ModelAndView("notfound1");
24 | modelAndView.addObject("id", exception.getId());
25 | return modelAndView;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/errors/src/main/java/bg/softuni/errors/web/NotFound2Controller.java:
--------------------------------------------------------------------------------
1 | package bg.softuni.errors.web;
2 |
3 | import bg.softuni.errors.exception.ObjectNotFoundException;
4 | import org.springframework.stereotype.Controller;
5 | import org.springframework.web.bind.annotation.GetMapping;
6 | import org.springframework.web.bind.annotation.PathVariable;
7 |
8 | @Controller
9 | public class NotFound2Controller {
10 |
11 | @GetMapping("/not-found2/{id}")
12 | public String notFound1(@PathVariable("id") String id) {
13 | throw new ObjectNotFoundException("Object with id " + id + " was not found!", id);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/errors/src/main/java/bg/softuni/errors/web/NotFound3Controller.java:
--------------------------------------------------------------------------------
1 | package bg.softuni.errors.web;
2 |
3 | import bg.softuni.errors.exception.ObjectNotFoundException;
4 | import org.springframework.stereotype.Controller;
5 | import org.springframework.web.bind.annotation.GetMapping;
6 | import org.springframework.web.bind.annotation.PathVariable;
7 |
8 | @Controller
9 | public class NotFound3Controller {
10 |
11 | @GetMapping("/not-found3/{id}")
12 | public String notFound1(@PathVariable("id") String id) {
13 | throw new ObjectNotFoundException("Object with id " + id + " was not found!", id);
14 | }
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/errors/src/main/java/bg/softuni/errors/web/TestRestController.java:
--------------------------------------------------------------------------------
1 | package bg.softuni.errors.web;
2 |
3 | import bg.softuni.errors.exception.ObjectNotFoundException;
4 | import bg.softuni.errors.exception.ObjectNotFoundRestException;
5 | import org.springframework.http.HttpStatus;
6 | import org.springframework.http.ResponseEntity;
7 | import org.springframework.web.bind.annotation.ExceptionHandler;
8 | import org.springframework.web.bind.annotation.GetMapping;
9 | import org.springframework.web.bind.annotation.ResponseStatus;
10 | import org.springframework.web.bind.annotation.RestController;
11 | import org.springframework.web.servlet.ModelAndView;
12 |
13 | @RestController
14 | public class TestRestController {
15 |
16 | @GetMapping("/test-rest")
17 | public ResponseEntity get() {
18 | throw new ObjectNotFoundRestException("Student not found");
19 | }
20 |
21 | @ResponseStatus(HttpStatus.NOT_FOUND)
22 | @ExceptionHandler(ObjectNotFoundRestException.class)
23 | public ErrorInfo handleNotFound(ObjectNotFoundRestException exception) {
24 | return new ErrorInfo(
25 | "/test-rest",
26 | exception
27 | );
28 | }
29 |
30 | }
31 |
32 | record Student(String name, int age){}
33 |
34 | class ErrorInfo {
35 | public final String url;
36 | public final String ex;
37 | public ErrorInfo(String url, Exception ex) {
38 | this.url = url;
39 | this.ex = ex.getMessage();
40 | }
41 | }
42 |
43 |
--------------------------------------------------------------------------------
/errors/src/main/java/bg/softuni/errors/web/WhitelabelTestController.java:
--------------------------------------------------------------------------------
1 | package bg.softuni.errors.web;
2 |
3 | import org.springframework.stereotype.Controller;
4 | import org.springframework.web.bind.annotation.GetMapping;
5 |
6 | @Controller
7 | public class WhitelabelTestController {
8 |
9 | @GetMapping("/test-whitelabel")
10 | public String testWhitelaabel() {
11 | throw new NullPointerException("Something went wrong!");
12 | }
13 |
14 | @GetMapping("/iae")
15 | public String testIae() {
16 | throw new IllegalArgumentException("iae");
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/errors/src/main/resources/application.yaml:
--------------------------------------------------------------------------------
1 | server:
2 | error:
3 | include-message: always
4 |
5 |
--------------------------------------------------------------------------------
/errors/src/main/resources/templates/error/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
7 |
8 | Document
9 |
10 |
11 |
12 | Uppps, custom 404 page!
13 |
14 |
15 |
--------------------------------------------------------------------------------
/errors/src/main/resources/templates/iae.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
7 |
8 | Document
9 |
10 |
11 |
12 | Illegal argument exception page!
13 |
14 |
15 |
--------------------------------------------------------------------------------
/errors/src/main/resources/templates/notfound-global.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
7 |
8 | Document
9 |
10 |
11 |
12 | Hello, this my GLOBAL custom object not found page!
13 | The object that was not found has id ID.
14 |
15 |
16 |
--------------------------------------------------------------------------------
/errors/src/main/resources/templates/notfound1.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
7 |
8 | Document
9 |
10 |
11 |
12 | Hello, this my custom object not found page!
13 | The object that was not found has id ID.
14 |
15 |
16 |
--------------------------------------------------------------------------------
/errors/src/test/java/bg/softuni/errors/ErrorsApplicationTests.java:
--------------------------------------------------------------------------------
1 | package bg.softuni.errors;
2 |
3 | import org.junit.jupiter.api.Test;
4 | import org.springframework.boot.test.context.SpringBootTest;
5 |
6 | @SpringBootTest
7 | class ErrorsApplicationTests {
8 |
9 | @Test
10 | void contextLoads() {
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/html/.gitignore:
--------------------------------------------------------------------------------
1 | ### IntelliJ IDEA ###
2 | .idea
3 |
--------------------------------------------------------------------------------
/html/home.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Welcome home!
5 |
6 |
--------------------------------------------------------------------------------
/html/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 |
--------------------------------------------------------------------------------
/html/simplejs.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/html/test.requests.http:
--------------------------------------------------------------------------------
1 | GET http://softuni.org
--------------------------------------------------------------------------------
/i18n/.gitignore:
--------------------------------------------------------------------------------
1 | HELP.md
2 | .gradle
3 | build/
4 | !gradle/wrapper/gradle-wrapper.jar
5 | !**/src/main/**/build/
6 | !**/src/test/**/build/
7 |
8 | ### STS ###
9 | .apt_generated
10 | .classpath
11 | .factorypath
12 | .project
13 | .settings
14 | .springBeans
15 | .sts4-cache
16 | bin/
17 | !**/src/main/**/bin/
18 | !**/src/test/**/bin/
19 |
20 | ### IntelliJ IDEA ###
21 | .idea
22 | *.iws
23 | *.iml
24 | *.ipr
25 | out/
26 | !**/src/main/**/out/
27 | !**/src/test/**/out/
28 |
29 | ### NetBeans ###
30 | /nbproject/private/
31 | /nbbuild/
32 | /dist/
33 | /nbdist/
34 | /.nb-gradle/
35 |
36 | ### VS Code ###
37 | .vscode/
38 |
--------------------------------------------------------------------------------
/i18n/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'java'
3 | id 'org.springframework.boot' version '2.7.16'
4 | id 'io.spring.dependency-management' version '1.1.3'
5 | }
6 |
7 | group = 'bg.softuni'
8 | version = '0.0.1-SNAPSHOT'
9 |
10 | java {
11 | sourceCompatibility = '17'
12 | }
13 |
14 | repositories {
15 | mavenCentral()
16 | }
17 |
18 | dependencies {
19 | implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
20 | implementation 'org.springframework.boot:spring-boot-starter-web'
21 | testImplementation 'org.springframework.boot:spring-boot-starter-test'
22 | }
23 |
24 | tasks.named('test') {
25 | useJUnitPlatform()
26 | }
27 |
--------------------------------------------------------------------------------
/i18n/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luchob/softuni-sep-2023/c640aaad1a3e34eba81af8ba5ed433ea3899f529/i18n/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/i18n/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-bin.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/i18n/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'i18n'
2 |
--------------------------------------------------------------------------------
/i18n/src/main/java/bg/softuni/i18n/I18nApplication.java:
--------------------------------------------------------------------------------
1 | package bg.softuni.i18n;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | @SpringBootApplication
7 | public class I18nApplication {
8 |
9 | public static void main(String[] args) {
10 | SpringApplication.run(I18nApplication.class, args);
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/i18n/src/main/java/bg/softuni/i18n/web/LangControllerCookies.java:
--------------------------------------------------------------------------------
1 | package bg.softuni.i18n.web;
2 |
3 | import javax.servlet.http.Cookie;
4 | import javax.servlet.http.HttpServletResponse;
5 | import org.springframework.stereotype.Controller;
6 | import org.springframework.ui.Model;
7 | import org.springframework.web.bind.annotation.CookieValue;
8 | import org.springframework.web.bind.annotation.GetMapping;
9 | import org.springframework.web.bind.annotation.PostMapping;
10 | import org.springframework.web.bind.annotation.RequestParam;
11 |
12 | @Controller
13 | public class LangControllerCookies {
14 |
15 |
16 | @GetMapping("/cookie")
17 | public String getLang(
18 | @CookieValue(
19 | value = "lang",
20 | defaultValue = "bg") String lang,
21 | Model model) {
22 |
23 | model.addAttribute("lang", lang);
24 |
25 | return "lang-view-cookie";
26 | }
27 |
28 | @PostMapping("/cookie")
29 | public String postLang(@RequestParam("lang") String lang,
30 | HttpServletResponse response) {
31 |
32 | Cookie cookie = new Cookie("lang", lang);
33 | response.addCookie(cookie);
34 |
35 | return "redirect:/cookie";
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/i18n/src/main/java/bg/softuni/i18n/web/LangControllerSessions.java:
--------------------------------------------------------------------------------
1 | package bg.softuni.i18n.web;
2 |
3 | import javax.servlet.http.HttpSession;
4 | import org.springframework.stereotype.Controller;
5 | import org.springframework.ui.Model;
6 | import org.springframework.web.bind.annotation.GetMapping;
7 | import org.springframework.web.bind.annotation.PostMapping;
8 | import org.springframework.web.bind.annotation.RequestParam;
9 |
10 | @Controller
11 | public class LangControllerSessions {
12 |
13 | @GetMapping("/session")
14 | public String getLang(
15 | HttpSession session,
16 | Model model) {
17 |
18 | var lang = session.getAttribute("lang");
19 | if (lang == null) {
20 | lang = "bg";
21 | }
22 |
23 | session.setAttribute("name", "Ivan");
24 |
25 | model.addAttribute("lang", lang);
26 |
27 | return "lang-view-session";
28 | }
29 |
30 | @PostMapping("/session")
31 | public String postLang(@RequestParam("lang") String lang,
32 | HttpSession session) {
33 |
34 | session.setAttribute("lang", lang);
35 |
36 | return "redirect:/session";
37 | }
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/i18n/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/i18n/src/main/resources/templates/lang-view-cookie.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
7 |
8 | Document
9 |
10 |
11 |
28 |
29 |
--------------------------------------------------------------------------------
/i18n/src/main/resources/templates/lang-view-session.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
7 |
8 | Document
9 |
10 |
11 |
28 |
29 |
30 | -----
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/i18n/src/test/java/bg/softuni/i18n/I18nApplicationTests.java:
--------------------------------------------------------------------------------
1 | package bg.softuni.i18n;
2 |
3 | import org.junit.jupiter.api.Test;
4 | import org.springframework.boot.test.context.SpringBootTest;
5 |
6 | @SpringBootTest
7 | class I18nApplicationTests {
8 |
9 | @Test
10 | void contextLoads() {
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/interview-questions/.gitignore:
--------------------------------------------------------------------------------
1 | ### IntelliJ IDEA ###
2 | .idea
3 |
--------------------------------------------------------------------------------
/interview-questions/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 | # Editor-based HTTP Client requests
5 | /httpRequests/
6 | # Datasource local storage ignored files
7 | /dataSources/
8 | /dataSources.local.xml
9 |
--------------------------------------------------------------------------------
/interview-questions/advanced/01-rest-api.md:
--------------------------------------------------------------------------------
1 | # Lecture REST API
2 |
3 | 1. За какво се използва REST?
4 | 2. Какви са архитектурните REST принципи? Какво означава всяка един от тях?
5 | 3. Какви са HTTP verbs които познавате и за какво служат?
6 | 4. Какво е HTTP status code? Какви съществуват и какво означават?
7 | 5. Как се създава контролер който връща REST респонс?
8 | 6. Какво представлява `@RestController` анотацията и какво я отличава от `@Controller`?
9 | 7. За какво се използва `ResponseEntity`?
10 | 8. Какво е HATEOAS?
--------------------------------------------------------------------------------
/interview-questions/advanced/02-spring-security.md:
--------------------------------------------------------------------------------
1 | # Lecture Spring security
2 |
3 | 1. За какво се използва spring security?
4 | 2. Как се инсталира спринг секюрити в servlet проект?
5 | 3. Какво е Filter и за какво се използва?
6 | 4. Каква е разликата между филтър и интерсептор?
7 | 5. Как се хешират паролите на потребителите, когато ги съхраняваме в базата данни?
8 | 6. При форм логин - кои са трите бийна, които трябва да се създадат?
9 | 7. Как управляваме достъпа до различните ресурси в приложението?
10 | 8. Какво представлява `@PreAuthorize` анотацията?
11 | 9. Каква е разликата между аутентикация и авторизация?
--------------------------------------------------------------------------------
/interview-questions/advanced/03-error-handling.md:
--------------------------------------------------------------------------------
1 | # Lecture - error handling
2 |
3 | 1. Какво представлява Whitelabel Error Page?
4 | 2. Как може да я заменим?
5 | 3. Как може да направим при грешка даден HTTP status code да се визуализира в определен thymeleaf template?
6 | 4. Как може да обработваме ексепшъни в рамките на един контролер?
7 | 5. Как може да обработваме глобални ексепшъни?
8 | 6. Как може да върнем специфичен JSON при грешка в REST API?
--------------------------------------------------------------------------------
/interview-questions/advanced/04-events.md:
--------------------------------------------------------------------------------
1 | # Events in spring
2 |
3 | 1. Кой е главния клас който подсугурява event handling в Spring?
4 | 2. Как се прави custom event?
5 | 3. Как може да обвържем event с транзакция?
6 | 4. Какво е scheduling?
7 | 5. Как може да имплементираме Scheduler в Spring?
8 | 6. Каква е разликата между cron, fix rate и fix delay?
9 | 7. Какво представлява spring cache?
10 | 8. Какво е прокси?
11 | 9. Какви проксита създава спринг?
12 | 10. Може ли в рамките на един сървис от нетранзакционен метод да извикаме транзакционен и ще работи ли това? Защо?
--------------------------------------------------------------------------------
/interview-questions/advanced/05-testing.md:
--------------------------------------------------------------------------------
1 | # Lecture - testing
2 |
3 | 1. Какво представлява един unit тест?
4 | 2. Как се създават депендънситата на класа, който се тества?
5 | 3. Какво представлява mockito?
6 | 4. Познавате ли интеграция между mockito и Junit?
7 | 5. Как се създава интегрейшън тест в Spring?
8 | 6. Какво представлява `@SpringBootTest` анотацията?
9 | 7. Какво е mockMvc?
10 | 8. Как да подаваме CSRF токен в mockMvc?
11 | 9. Как да емулираме логнат потребител в spring integration test?
12 | 10. Разликата между TDD и code-first approach?
--------------------------------------------------------------------------------
/interview-questions/advanced/06-deployment-monitoring.md:
--------------------------------------------------------------------------------
1 | # Deployment, hosting, monitoring
2 |
3 | 1. Какво представлява spring actuator?
4 | 2. Как може да се конфигурира actuator - да се разрешават или забраняват определени ендпойнти?
5 | 3. На същия порт като приложението ли работи actuator?
6 | 4. Какво представлява micrometer?
7 | 5. Как може да експортваме метрики с помощта на micrometer? Познавате ли подобни технологии?
--------------------------------------------------------------------------------
/interview-questions/advanced/07-aop.md:
--------------------------------------------------------------------------------
1 | # AOP
2 |
3 | 1. Какво представлява AOP?
4 | 2. Какво е Aspect, Advise, Pointcut, Join Point?
5 | 3. Какво е таргет обект и AOP Proxy?
6 | 4. Използва ли сприг цялата функционалност на AspectJ?
7 | 5. Какви проксита създава Spring и как с тях се имплементира AOP?
8 | 6. Кога се предпочита CGLIB и кога dynamic JDK proxy?
9 | 7. Може ли да се слагат AOP анотации върху final методи, когато се ползва CGLIB?
--------------------------------------------------------------------------------
/interview-questions/advanced/08-kafka.md:
--------------------------------------------------------------------------------
1 | # Kafka
2 |
3 | 1. Какво представлява Kafka?
4 | 2. Как се наричат сървърите в Kafka?
5 | 3. Какво е producer и consumer?
6 | 4. Къде се пазят съобщенията в кафка?
7 | 5. Как се разделя един topic?
8 | 6. Всички партишъни на един топик се съхраняват на един или на различни брокери?
9 | 7. Какво представлява consumer group?
10 | 8. Къде се пазят offset-ите на consumer-ите?
--------------------------------------------------------------------------------
/interview-questions/fundamentals/01-internet-explained.md:
--------------------------------------------------------------------------------
1 | # Internet explained
2 |
3 | 1. Каква е целта на транспортния слой?
4 | 2. Опишете разликата между TCP и UDP.
5 | 3. Каква е ролята на IP протоколът?
6 | 4. IPv4 и IPv6 - какво е новото и защо искаме IPv6?
7 | 5. IPv4 класове срещу CIDR. Какво е новото и защо се е наложило?
8 | 6. Какво е TTL в IP?
9 | 7. Какво представлява OSI моделът? Какво е TCP/IP модел?
10 |
--------------------------------------------------------------------------------
/interview-questions/fundamentals/02-http-protocol.md:
--------------------------------------------------------------------------------
1 | # HTTP Protocol
2 |
3 | 1. Какви HTTP "глаголи" познавате?
4 | 2. Каква е разликата между PUT, POST и PATCH?
5 | 3. Какво е предназначението на всяка от следните групи HTTP статуси - 1xx, 2xx, 3xx, 4xx и 5xx?
6 | 4. Избройте някои по-често срещани HTTP status-и?
7 | 5. Какво е HTTP/2 и с какво подобрява HTTP/1.1?
8 | 6. Какви недостатъци има HTTP/2?
9 | 7. Какво представлява HTTP/3?
--------------------------------------------------------------------------------
/interview-questions/fundamentals/03-spring-boot.md:
--------------------------------------------------------------------------------
1 | # Lecture Spring Boot
2 |
3 | 1. Какво е IoC и DI? Какво е Spring IoC контейнер?
4 | 2. Има ли разлика между Spring Boot и Spring MVC?
5 | 3. Какво е Spring Boot Starter?
6 | 4. Какво е Spring Boot Autoconfiguration?
7 | 5. Какво е Spring Boot Actuator?
8 | 6. Как може да създадем един Spring Boot проект?
9 | 7. Дайте примери за няколко Spring Boot Starter-а.
--------------------------------------------------------------------------------
/interview-questions/fundamentals/04-spring-mvc.md:
--------------------------------------------------------------------------------
1 | # Lecture Spring MVC
2 |
3 | 1. Какво е MVC?
4 | 2. Какво разбирате под "фронтенд контролер"?
5 | 2. Какво е DispatcherServlet?
6 | 3. Как може да предадем атрибути към едно view?
7 | 4. Слоеве в типичен spring application.
8 | 5. Как може да реализираме обработката на Get/Post и т.н. заявки в контролер?
9 | 6. Какво ще стане ако пратим POST заявка към ендпойнт, който може да обработва само GET заявки?
--------------------------------------------------------------------------------
/interview-questions/fundamentals/06-state.md:
--------------------------------------------------------------------------------
1 | # Lecture state management
2 |
3 | 1. Какво означава cookie?
4 | 2. Какво е scope на едно cookie?
5 | 3. Какво е съдържанието на едно cookie?
6 | 4. Какво представлява HTTP сесията?
7 | 5. Как се създава и унищожава една HTTP сесия?
8 | 6. С помощта на кои класове и анотации работим със сесии в Spring MVC?
9 | 7. С помощта на кои класове и анотации работим със cookies в Spring MVC?
10 | 8. Какво е 3rd party cookie?
--------------------------------------------------------------------------------
/interview-questions/fundamentals/07-thymeleaf.md:
--------------------------------------------------------------------------------
1 | # Lecture Thymeleaf
2 |
3 | 1. Какво представлява Thymeleaf engine?
4 | 2. Какви алтернативи на Thymeleaf познавате?
5 | 3. Какво добавя Spring към Thymeleaf?
6 | 4. Как се реализира if-else statement в Thymealeaf?
7 | 5. Как се реализират цикли в Thymeleaf?
8 | 6. Поддържа ли Thymeleaf elvis оператор?
9 | 7. Как се изкарва повтаряем markup в Thymeleaf?
10 |
--------------------------------------------------------------------------------
/interview-questions/fundamentals/08-spring-essentials.md:
--------------------------------------------------------------------------------
1 | # Lecture Spring Essentials
2 |
3 | 1. За какво се използва `@ModelAttribute` анотацията?
4 | 2. Какво е CORS?
5 | 3. Как се управлява CORS в Spring MVC?
6 | 4. Ако имаме два бийна (например. Car и Bike), които имплементират общ интерфейс (напр. Vehicle) как може да инжектираме само 1 от тях?
7 | 5. Как се чете cookie?
8 | 6. Как се създава cookie?
9 | 7. Как се трие cookie?
10 | 8. Как се чете HTTP header?
--------------------------------------------------------------------------------
/kafka-consumer/.gitignore:
--------------------------------------------------------------------------------
1 | HELP.md
2 | .gradle
3 | build/
4 | !gradle/wrapper/gradle-wrapper.jar
5 | !**/src/main/**/build/
6 | !**/src/test/**/build/
7 |
8 | ### STS ###
9 | .apt_generated
10 | .classpath
11 | .factorypath
12 | .project
13 | .settings
14 | .springBeans
15 | .sts4-cache
16 | bin/
17 | !**/src/main/**/bin/
18 | !**/src/test/**/bin/
19 |
20 | ### IntelliJ IDEA ###
21 | .idea
22 | *.iws
23 | *.iml
24 | *.ipr
25 | out/
26 | !**/src/main/**/out/
27 | !**/src/test/**/out/
28 |
29 | ### NetBeans ###
30 | /nbproject/private/
31 | /nbbuild/
32 | /dist/
33 | /nbdist/
34 | /.nb-gradle/
35 |
36 | ### VS Code ###
37 | .vscode/
38 |
--------------------------------------------------------------------------------
/kafka-consumer/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'java'
3 | id 'org.springframework.boot' version '3.1.5'
4 | id 'io.spring.dependency-management' version '1.1.3'
5 | }
6 |
7 | group = 'bg.softuni'
8 | version = '0.0.1-SNAPSHOT'
9 |
10 | java {
11 | sourceCompatibility = '17'
12 | }
13 |
14 | repositories {
15 | mavenCentral()
16 | }
17 |
18 | dependencies {
19 | implementation 'org.springframework.boot:spring-boot-starter-web'
20 | implementation 'org.springframework.kafka:spring-kafka'
21 | testImplementation 'org.springframework.boot:spring-boot-starter-test'
22 | testImplementation 'org.springframework.kafka:spring-kafka-test'
23 | }
24 |
25 | tasks.named('bootBuildImage') {
26 | builder = 'paketobuildpacks/builder-jammy-base:latest'
27 | }
28 |
29 | tasks.named('test') {
30 | useJUnitPlatform()
31 | }
32 |
--------------------------------------------------------------------------------
/kafka-consumer/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luchob/softuni-sep-2023/c640aaad1a3e34eba81af8ba5ed433ea3899f529/kafka-consumer/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/kafka-consumer/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/kafka-consumer/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'kafka-consumer'
2 |
--------------------------------------------------------------------------------
/kafka-consumer/src/main/java/bg/softuni/kafka/KafkaConsumerApplication.java:
--------------------------------------------------------------------------------
1 | package bg.softuni.kafka;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | @SpringBootApplication
7 | public class KafkaConsumerApplication {
8 |
9 | public static void main(String[] args) {
10 | SpringApplication.run(KafkaConsumerApplication.class, args);
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/kafka-consumer/src/main/java/bg/softuni/kafka/config/KafkaConfig.java:
--------------------------------------------------------------------------------
1 | package bg.softuni.kafka.config;
2 |
3 | import bg.softuni.kafka.model.ExchangeRatesDTO;
4 | import org.springframework.boot.autoconfigure.kafka.KafkaProperties;
5 | import org.springframework.context.annotation.Bean;
6 | import org.springframework.context.annotation.Configuration;
7 | import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory;
8 | import org.springframework.kafka.core.ConsumerFactory;
9 | import org.springframework.kafka.core.DefaultKafkaConsumerFactory;
10 |
11 | @Configuration
12 | public class KafkaConfig {
13 |
14 | @Bean
15 | public ConsumerFactory consumerFactory(KafkaProperties kafkaProperties) {
16 | return new DefaultKafkaConsumerFactory<>(kafkaProperties.buildConsumerProperties());
17 | }
18 |
19 |
20 | @Bean
21 | public ConcurrentKafkaListenerContainerFactory kafkaContainerFactory(
22 | ConsumerFactory consumerFactory
23 | ) {
24 | ConcurrentKafkaListenerContainerFactory containerFactory =
25 | new ConcurrentKafkaListenerContainerFactory<>();
26 | containerFactory.setConsumerFactory(consumerFactory);
27 | return containerFactory;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/kafka-consumer/src/main/java/bg/softuni/kafka/consumer/ExRatesConsumer.java:
--------------------------------------------------------------------------------
1 | package bg.softuni.kafka.consumer;
2 |
3 | import bg.softuni.kafka.model.ExchangeRatesDTO;
4 | import org.slf4j.Logger;
5 | import org.slf4j.LoggerFactory;
6 | import org.springframework.kafka.annotation.KafkaListener;
7 | import org.springframework.kafka.support.KafkaHeaders;
8 | import org.springframework.messaging.handler.annotation.Header;
9 | import org.springframework.stereotype.Component;
10 |
11 | @Component
12 | public class ExRatesConsumer {
13 |
14 | private static Logger LOGGER = LoggerFactory.getLogger(ExRatesConsumer.class);
15 |
16 | @KafkaListener(
17 | containerFactory = "kafkaContainerFactory",
18 | topics = "exchange_rates",
19 | groupId = "softuni-consumer"
20 | )
21 | public void readMessage(
22 | @Header(KafkaHeaders.RECEIVED_KEY) String key,
23 | ExchangeRatesDTO exRate) {
24 |
25 | LOGGER.info("We have consumed ex rate with key{} and value {}.",
26 | key,
27 | exRate);
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/kafka-consumer/src/main/java/bg/softuni/kafka/model/ExchangeRatesDTO.java:
--------------------------------------------------------------------------------
1 | package bg.softuni.kafka.model;
2 |
3 | import java.math.BigDecimal;
4 | import java.time.LocalDateTime;
5 | import java.util.Map;
6 |
7 | //{
8 | // "base": "USD",
9 | // "rates": {
10 | // "BGN": 1.840515,
11 | // "EUR": 0.937668
12 | // }
13 | // }
14 | public record ExchangeRatesDTO(String base,
15 | long timestamp,
16 | Map rates) {
17 | }
18 |
--------------------------------------------------------------------------------
/kafka-consumer/src/main/resources/application.yaml:
--------------------------------------------------------------------------------
1 | spring:
2 | kafka:
3 | bootstrap-servers: localhost:9092
4 | consumer:
5 | key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
6 | value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer
7 | auto-offset-reset: earliest
8 | enable-auto-commit: false
9 | properties:
10 | spring.json.trusted.packages: '*'
11 | listener:
12 | concurrency: 2
13 | server:
14 | port: 7070
--------------------------------------------------------------------------------
/kafka-consumer/src/test/java/bg/softuni/kafka/KafkaConsumerApplicationTests.java:
--------------------------------------------------------------------------------
1 | package bg.softuni.kafka;
2 |
3 | import org.junit.jupiter.api.Test;
4 | import org.springframework.boot.test.context.SpringBootTest;
5 |
6 | @SpringBootTest
7 | class KafkaConsumerApplicationTests {
8 |
9 | @Test
10 | void contextLoads() {
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/kafka-producer/.gitignore:
--------------------------------------------------------------------------------
1 | HELP.md
2 | .gradle
3 | build/
4 | !gradle/wrapper/gradle-wrapper.jar
5 | !**/src/main/**/build/
6 | !**/src/test/**/build/
7 |
8 | ### STS ###
9 | .apt_generated
10 | .classpath
11 | .factorypath
12 | .project
13 | .settings
14 | .springBeans
15 | .sts4-cache
16 | bin/
17 | !**/src/main/**/bin/
18 | !**/src/test/**/bin/
19 |
20 | ### IntelliJ IDEA ###
21 | .idea
22 | *.iws
23 | *.iml
24 | *.ipr
25 | out/
26 | !**/src/main/**/out/
27 | !**/src/test/**/out/
28 |
29 | ### NetBeans ###
30 | /nbproject/private/
31 | /nbbuild/
32 | /dist/
33 | /nbdist/
34 | /.nb-gradle/
35 |
36 | ### VS Code ###
37 | .vscode/
38 |
--------------------------------------------------------------------------------
/kafka-producer/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'java'
3 | id 'org.springframework.boot' version '3.1.5'
4 | id 'io.spring.dependency-management' version '1.1.3'
5 | }
6 |
7 | group = 'bg.softuni'
8 | version = '0.0.1-SNAPSHOT'
9 |
10 | java {
11 | sourceCompatibility = '17'
12 | }
13 |
14 | repositories {
15 | mavenCentral()
16 | }
17 |
18 | dependencies {
19 | implementation 'org.springframework.boot:spring-boot-starter-web'
20 | implementation 'org.springframework.kafka:spring-kafka'
21 | testImplementation 'org.springframework.boot:spring-boot-starter-test'
22 | testImplementation 'org.springframework.kafka:spring-kafka-test'
23 | }
24 |
25 | tasks.named('bootBuildImage') {
26 | builder = 'paketobuildpacks/builder-jammy-base:latest'
27 | }
28 |
29 | tasks.named('test') {
30 | useJUnitPlatform()
31 | }
32 |
--------------------------------------------------------------------------------
/kafka-producer/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luchob/softuni-sep-2023/c640aaad1a3e34eba81af8ba5ed433ea3899f529/kafka-producer/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/kafka-producer/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/kafka-producer/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'kafka'
2 |
--------------------------------------------------------------------------------
/kafka-producer/src/main/java/bg/softuni/kafka/KafkaApplication.java:
--------------------------------------------------------------------------------
1 | package bg.softuni.kafka;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 | import org.springframework.kafka.support.serializer.JsonSerializer;
6 |
7 | @SpringBootApplication
8 | public class KafkaApplication {
9 |
10 | public static void main(String[] args) {
11 | SpringApplication.run(KafkaApplication.class, args);
12 | }
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/kafka-producer/src/main/java/bg/softuni/kafka/config/KafkaConfig.java:
--------------------------------------------------------------------------------
1 | package bg.softuni.kafka.config;
2 |
3 | import org.apache.kafka.clients.admin.NewTopic;
4 | import org.springframework.boot.autoconfigure.kafka.KafkaProperties;
5 | import org.springframework.context.annotation.Bean;
6 | import org.springframework.context.annotation.Configuration;
7 | import org.springframework.kafka.config.TopicBuilder;
8 | import org.springframework.kafka.core.DefaultKafkaProducerFactory;
9 | import org.springframework.kafka.core.KafkaTemplate;
10 | import org.springframework.kafka.core.ProducerFactory;
11 |
12 | @Configuration
13 | public class KafkaConfig {
14 |
15 | public static String EXCHANGE_RATE_TOPIC = "exchange_rates";
16 |
17 | @Bean
18 | public NewTopic exRateTopic() {
19 | return TopicBuilder.name("exchange_rates")
20 | .partitions(2)
21 | .build();
22 | }
23 |
24 | @Bean
25 | public ProducerFactory producerFactory(KafkaProperties kafkaProperties) {
26 | return new DefaultKafkaProducerFactory<>(
27 | kafkaProperties.buildProducerProperties()
28 | );
29 | }
30 |
31 | @Bean
32 | public KafkaTemplate jsonKafkaTemplate(
33 | ProducerFactory producerFactory) {
34 | return new KafkaTemplate<>(producerFactory);
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/kafka-producer/src/main/java/bg/softuni/kafka/model/ExchangeRatesDTO.java:
--------------------------------------------------------------------------------
1 | package bg.softuni.kafka.model;
2 |
3 | import java.math.BigDecimal;
4 | import java.time.LocalDateTime;
5 | import java.util.Map;
6 |
7 | //{
8 | // "base": "USD",
9 | // "rates": {
10 | // "BGN": 1.840515,
11 | // "EUR": 0.937668
12 | // }
13 | // }
14 | public record ExchangeRatesDTO(String base,
15 | long timestamp,
16 | Map rates) {
17 | }
18 |
--------------------------------------------------------------------------------
/kafka-producer/src/main/java/bg/softuni/kafka/service/KafkaPublicationService.java:
--------------------------------------------------------------------------------
1 | package bg.softuni.kafka.service;
2 |
3 | import static bg.softuni.kafka.config.KafkaConfig.EXCHANGE_RATE_TOPIC;
4 |
5 | import bg.softuni.kafka.model.ExchangeRatesDTO;
6 | import java.util.UUID;
7 | import org.slf4j.Logger;
8 | import org.slf4j.LoggerFactory;
9 | import org.springframework.kafka.core.KafkaTemplate;
10 | import org.springframework.stereotype.Service;
11 |
12 | @Service
13 | public class KafkaPublicationService {
14 |
15 | private Logger LOGGER = LoggerFactory.getLogger(KafkaPublicationService.class);
16 | private final KafkaTemplate kafkaTemplate;
17 |
18 | public KafkaPublicationService(KafkaTemplate kafkaTemplate) {
19 | this.kafkaTemplate = kafkaTemplate;
20 | }
21 |
22 | public void publishExchangeRate(ExchangeRatesDTO exchangeRatesDTO) {
23 | kafkaTemplate.
24 | send(EXCHANGE_RATE_TOPIC, UUID.randomUUID().toString(), exchangeRatesDTO).
25 | whenComplete(
26 | (res, ex) -> {
27 | if (ex == null) {
28 | LOGGER.info(
29 | "Kafka message successfully send to topic {}/partition {}/ offset {}. Key = {}.",
30 | res.getRecordMetadata().topic(),
31 | res.getRecordMetadata().partition(),
32 | res.getRecordMetadata().offset(),
33 | res.getProducerRecord().key()
34 | );
35 | } else {
36 | LOGGER.error("Problem with the publication to kafka.", ex);
37 | }
38 | }
39 |
40 | );
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/kafka-producer/src/main/java/bg/softuni/kafka/web/FakePublisherController.java:
--------------------------------------------------------------------------------
1 | package bg.softuni.kafka.web;
2 |
3 | import bg.softuni.kafka.model.ExchangeRatesDTO;
4 | import bg.softuni.kafka.service.KafkaPublicationService;
5 | import java.math.BigDecimal;
6 | import java.time.LocalDateTime;
7 | import java.util.Map;
8 | import org.springframework.web.bind.annotation.GetMapping;
9 | import org.springframework.web.bind.annotation.RestController;
10 |
11 | @RestController
12 | public class FakePublisherController {
13 |
14 | private final KafkaPublicationService kafkaPublicationService;
15 |
16 | public FakePublisherController(KafkaPublicationService kafkaPublicationService) {
17 | this.kafkaPublicationService = kafkaPublicationService;
18 | }
19 |
20 | @GetMapping("/publish")
21 | public String publish() {
22 | var toPublish = new ExchangeRatesDTO(
23 | "USD",
24 | System.currentTimeMillis(),
25 | Map.of("BGN", BigDecimal.valueOf(1.840515),
26 | "EUR", BigDecimal.valueOf(0.937668)
27 | )
28 | );
29 |
30 | kafkaPublicationService.publishExchangeRate(toPublish);
31 |
32 | return "OK";
33 | }
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/kafka-producer/src/main/resources/application.yaml:
--------------------------------------------------------------------------------
1 | spring:
2 | kafka:
3 | producer:
4 | key-serializer: org.apache.kafka.common.serialization.StringSerializer
5 | value-serializer: org.springframework.kafka.support.serializer.JsonSerializer
6 | bootstrap-servers: localhost:9092
7 |
--------------------------------------------------------------------------------
/kafka-producer/src/test/java/bg/softuni/kafka/KafkaApplicationTests.java:
--------------------------------------------------------------------------------
1 | package bg.softuni.kafka;
2 |
3 | import org.junit.jupiter.api.Test;
4 | import org.springframework.boot.test.context.SpringBootTest;
5 |
6 | @SpringBootTest
7 | class KafkaApplicationTests {
8 |
9 | @Test
10 | void contextLoads() {
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/local/docker-mysql-apple-m1.yaml:
--------------------------------------------------------------------------------
1 | version: '3.3'
2 | services:
3 | db:
4 | image: arm64v8/mysql:oracle
5 | ports:
6 | - "3306:3306"
7 | command: ['--character-set-server=utf8mb4', '--collation-server=utf8mb4_bin', '--default-authentication-plugin=mysql_native_password']
8 | environment:
9 | - MYSQL_ALLOW_EMPTY_PASSWORD="yes"
10 | - MYSQL_DATABASE=mobilele
11 | mailhog:
12 | image: mailhog/mailhog
13 | logging:
14 | driver: 'none' # disable saving logs
15 | ports:
16 | - "1025:1025" # smtp server
17 | - "8025:8025" # web ui
18 | # redis:
19 | # image: redis
20 | # ports:
21 | # - "6379:6379"
22 | promtool:
23 | image: prom/prometheus:v2.43.0
24 | ports:
25 | - 9090:9090
26 | volumes:
27 | - type: bind
28 | source: ./prometheus.yml
29 | target: /etc/prometheus/prometheus.yml
30 | grafana:
31 | image: grafana/grafana:8.5.22
32 | ports:
33 | - 3000:3000
34 | zookeeper:
35 | image: wurstmeister/zookeeper
36 | ports:
37 | - "2181:2181"
38 | kafka:
39 | image: wurstmeister/kafka
40 | ports:
41 | - "9092:9092"
42 | environment:
43 | KAFKA_ADVERTISED_HOST_NAME: 127.0.0.1
44 | KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
45 | volumes:
46 | - /var/run/docker.sock:/var/run/docker.sock
--------------------------------------------------------------------------------
/local/prometheus.yml:
--------------------------------------------------------------------------------
1 | # check sample config here
2 | # https://github.com/prometheus/prometheus/blob/release-2.1/config/testdata/conf.good.yml
3 |
4 | # my global config
5 | global:
6 | scrape_interval: 5s
7 | evaluation_interval: 15s
8 |
9 | scrape_configs:
10 | - job_name: rest-service
11 |
12 | honor_labels: true
13 | # scrape_interval is defined by the configured global (15s).
14 | # scrape_timeout is defined by the global default (10s).
15 |
16 | # metrics_path defaults to '/metrics'
17 | # scheme defaults to 'http'.
18 |
19 | metrics_path: /actuator/prometheus
20 | scheme: http
21 |
22 | static_configs:
23 | - targets: ['host.docker.internal:8081']
--------------------------------------------------------------------------------
/mobilele/.gitignore:
--------------------------------------------------------------------------------
1 | HELP.md
2 | .gradle
3 | build/
4 | !gradle/wrapper/gradle-wrapper.jar
5 | !**/src/main/**/build/
6 | !**/src/test/**/build/
7 |
8 | ### STS ###
9 | .apt_generated
10 | .classpath
11 | .factorypath
12 | .project
13 | .settings
14 | .springBeans
15 | .sts4-cache
16 | bin/
17 | !**/src/main/**/bin/
18 | !**/src/test/**/bin/
19 |
20 | ### IntelliJ IDEA ###
21 | .idea
22 | *.iws
23 | *.iml
24 | *.ipr
25 | out/
26 | !**/src/main/**/out/
27 | !**/src/test/**/out/
28 |
29 | ### NetBeans ###
30 | /nbproject/private/
31 | /nbbuild/
32 | /dist/
33 | /nbdist/
34 | /.nb-gradle/
35 |
36 | ### VS Code ###
37 | .vscode/
38 |
--------------------------------------------------------------------------------
/mobilele/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'java'
3 | id 'org.springframework.boot' version '3.1.3'
4 | id 'io.spring.dependency-management' version '1.1.3'
5 | }
6 |
7 | group = 'org.softuni'
8 |
9 | java {
10 | sourceCompatibility = '17'
11 | }
12 |
13 | repositories {
14 | mavenCentral()
15 | }
16 |
17 | dependencies {
18 |
19 | implementation 'org.springframework.boot:spring-boot-starter-security'
20 | implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
21 | implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
22 | implementation 'org.springframework.boot:spring-boot-starter-mail'
23 | implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6'
24 | implementation 'org.springframework.boot:spring-boot-starter-cache'
25 | implementation 'org.springframework.boot:spring-boot-starter-actuator'
26 | implementation 'io.micrometer:micrometer-registry-prometheus'
27 | implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.2.0'
28 | implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
29 |
30 | implementation 'org.springframework.boot:spring-boot-starter-validation'
31 | implementation 'org.springframework.boot:spring-boot-starter-web'
32 | implementation 'org.springframework.boot:spring-boot-starter-webflux'
33 | runtimeOnly 'com.mysql:mysql-connector-j'
34 | testImplementation 'com.icegreen:greenmail:2.0.0'
35 | testImplementation 'org.springframework.boot:spring-boot-starter-test'
36 | testImplementation 'org.springframework.security:spring-security-test'
37 | testImplementation 'org.hsqldb:hsqldb'
38 | }
39 |
40 | tasks.named('test') {
41 | useJUnitPlatform()
42 | }
43 |
--------------------------------------------------------------------------------
/mobilele/deployment/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM amazoncorretto:17.0.6
2 |
3 | COPY build/libs/mobilele.jar app.jar
4 |
5 | ENTRYPOINT ["java", "-jar", "/app.jar"]
--------------------------------------------------------------------------------
/mobilele/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luchob/softuni-sep-2023/c640aaad1a3e34eba81af8ba5ed433ea3899f529/mobilele/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/mobilele/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-bin.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/mobilele/sample-docker-commands.md:
--------------------------------------------------------------------------------
1 | ```
2 | docker network create softuni
3 |
4 | docker run \
5 | -e MYSQL_ALLOW_EMPTY_PASSWORD='yes' \
6 | -e MYSQL_DATABASE='mobilele' \
7 | --hostname=db \
8 | --network=softuni \
9 | -p 3306:3306 \
10 | arm64v8/mysql:oracle --character-set-server=utf8mb4 --collation-server=utf8mb4_bin --default-authentication-plugin=mysql_native_password
11 |
12 | docker run \
13 | -e MYSQL_HOST='db' \
14 | -e MYSQL_USER='root' \
15 | -e MYSQL_PASSWORD='' \
16 | -p 8080:8080 \
17 | --hostname=mobilele \
18 | --network=softuni \
19 | luchob/mobilele
20 | ```
--------------------------------------------------------------------------------
/mobilele/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'mobilele'
2 |
--------------------------------------------------------------------------------
/mobilele/src/main/java/org/softuni/mobilele/MobileleApplication.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele;
2 |
3 | import io.swagger.v3.oas.annotations.OpenAPIDefinition;
4 | import io.swagger.v3.oas.annotations.info.Info;
5 | import io.swagger.v3.oas.annotations.servers.Server;
6 | import org.springframework.boot.SpringApplication;
7 | import org.springframework.boot.autoconfigure.SpringBootApplication;
8 | import org.springframework.cache.annotation.EnableCaching;
9 | import org.springframework.scheduling.annotation.EnableScheduling;
10 |
11 | @OpenAPIDefinition(
12 | info = @Info(
13 | title = "Mobilele",
14 | version = "0.0.1",
15 | description = "The REST API of mobilele"
16 | ),
17 | servers = {
18 | @Server(
19 | url = "http://localhost:8080",
20 | description = "Local server"
21 | )
22 | }
23 | )
24 | @EnableScheduling
25 | @EnableCaching
26 | @SpringBootApplication
27 | public class MobileleApplication {
28 |
29 | public static void main(String[] args) {
30 | SpringApplication.run(MobileleApplication.class, args);
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/mobilele/src/main/java/org/softuni/mobilele/config/AppConfig.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele.config;
2 |
3 | import org.springframework.boot.web.client.RestTemplateBuilder;
4 | import org.springframework.context.annotation.Bean;
5 | import org.springframework.context.annotation.Configuration;
6 | import org.springframework.http.HttpHeaders;
7 | import org.springframework.http.MediaType;
8 | import org.springframework.security.crypto.password.PasswordEncoder;
9 | import org.springframework.security.crypto.password.Pbkdf2PasswordEncoder;
10 | import org.springframework.web.client.RestTemplate;
11 | import org.springframework.web.reactive.function.client.WebClient;
12 |
13 | @Configuration
14 | public class AppConfig {
15 |
16 | @Bean
17 | public WebClient formPostWebClient() {
18 | return WebClient
19 | .builder()
20 | .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE)
21 | .build();
22 | }
23 |
24 | @Bean
25 | public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {
26 | return restTemplateBuilder
27 | .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
28 | .build();
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/mobilele/src/main/java/org/softuni/mobilele/config/MailConfiguration.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele.config;
2 |
3 | import java.util.Properties;
4 | import org.springframework.beans.factory.annotation.Value;
5 | import org.springframework.context.annotation.Bean;
6 | import org.springframework.context.annotation.Configuration;
7 | import org.springframework.mail.javamail.JavaMailSender;
8 | import org.springframework.mail.javamail.JavaMailSenderImpl;
9 |
10 | @Configuration
11 | public class MailConfiguration {
12 |
13 | @Bean
14 | public JavaMailSender javaMailSender(
15 | @Value("${mail.host}") String host,
16 | @Value("${mail.port}") int port,
17 | @Value("${mail.username}") String username,
18 | @Value("${mail.password}") String password) {
19 | JavaMailSenderImpl javaMailSender = new JavaMailSenderImpl();
20 |
21 | javaMailSender.setHost(host);
22 | javaMailSender.setPort(port);
23 | javaMailSender.setUsername(username);
24 | javaMailSender.setPassword(password);
25 | javaMailSender.setDefaultEncoding("UTF-8");
26 | javaMailSender.setJavaMailProperties(mailProperties());
27 |
28 | return javaMailSender;
29 | }
30 |
31 | private Properties mailProperties() {
32 | Properties properties = new Properties();
33 |
34 | properties.setProperty("mail.smtp.auth", "true");
35 | properties.setProperty("mail.transport.protocol", "smtp");
36 |
37 | return properties;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/mobilele/src/main/java/org/softuni/mobilele/config/OpenExchangeRateConfig.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele.config;
2 |
3 | import java.util.List;
4 | import org.springframework.boot.context.properties.ConfigurationProperties;
5 | import org.springframework.context.annotation.Configuration;
6 |
7 | @Configuration
8 | @ConfigurationProperties(prefix = "open.exchange.rates")
9 | public class OpenExchangeRateConfig {
10 |
11 | private String appId;
12 | private List symbols;
13 | private String host;
14 | private String schema;
15 | private String path;
16 | private boolean enabled;
17 |
18 | public String getPath() {
19 | return path;
20 | }
21 |
22 | public OpenExchangeRateConfig setPath(String path) {
23 | this.path = path;
24 | return this;
25 | }
26 |
27 | public String getAppId() {
28 | return appId;
29 | }
30 |
31 | public OpenExchangeRateConfig setAppId(String appId) {
32 | this.appId = appId;
33 | return this;
34 | }
35 |
36 | public List getSymbols() {
37 | return symbols;
38 | }
39 |
40 | public boolean isEnabled() {
41 | return enabled;
42 | }
43 |
44 | public OpenExchangeRateConfig setEnabled(boolean enabled) {
45 | this.enabled = enabled;
46 | return this;
47 | }
48 |
49 | public OpenExchangeRateConfig setSymbols(List symbols) {
50 | this.symbols = symbols;
51 | return this;
52 | }
53 |
54 | public String getHost() {
55 | return host;
56 | }
57 |
58 | public OpenExchangeRateConfig setHost(String host) {
59 | this.host = host;
60 | return this;
61 | }
62 |
63 | public String getSchema() {
64 | return schema;
65 | }
66 |
67 | public OpenExchangeRateConfig setSchema(String schema) {
68 | this.schema = schema;
69 | return this;
70 | }
71 |
72 | @Override
73 | public String toString() {
74 | return "OpenExchangeRateConfig{" +
75 | "appId='" + appId + '\'' +
76 | ", symbols=" + symbols +
77 | ", host='" + host + '\'' +
78 | ", schema='" + schema + '\'' +
79 | ", path='" + path + '\'' +
80 | ", enabled=" + enabled +
81 | '}';
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/mobilele/src/main/java/org/softuni/mobilele/config/ReCaptchaConfig.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele.config;
2 |
3 | import org.springframework.boot.context.properties.ConfigurationProperties;
4 | import org.springframework.context.annotation.Configuration;
5 |
6 | @Configuration
7 | @ConfigurationProperties(prefix = "google.recaptcha")
8 | public class ReCaptchaConfig {
9 |
10 | private String site;
11 | private String secret;
12 |
13 | public String getSite() {
14 | return site;
15 | }
16 |
17 | public ReCaptchaConfig setSite(String site) {
18 | this.site = site;
19 | return this;
20 | }
21 |
22 | public String getSecret() {
23 | return secret;
24 | }
25 |
26 | public ReCaptchaConfig setSecret(String secret) {
27 | this.secret = secret;
28 | return this;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/mobilele/src/main/java/org/softuni/mobilele/init/RatesInit.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele.init;
2 |
3 | import java.util.HashMap;
4 | import java.util.Map;
5 | import java.util.stream.Collectors;
6 | import org.softuni.mobilele.config.OpenExchangeRateConfig;
7 | import org.softuni.mobilele.model.dto.ExchangeRatesDTO;
8 | import org.softuni.mobilele.service.CurrencyService;
9 | import org.springframework.boot.CommandLineRunner;
10 | import org.springframework.stereotype.Component;
11 | import org.springframework.web.client.RestTemplate;
12 |
13 | @Component
14 | public class RatesInit implements CommandLineRunner {
15 |
16 | private final RestTemplate restTemplate;
17 | private final OpenExchangeRateConfig openExchangeRateConfig;
18 |
19 | private final CurrencyService currencyService;
20 |
21 | public RatesInit(
22 | RestTemplate restTemplate,
23 | OpenExchangeRateConfig openExchangeRateConfig,
24 | CurrencyService currencyService) {
25 | this.restTemplate = restTemplate;
26 | this.openExchangeRateConfig = openExchangeRateConfig;
27 | this.currencyService = currencyService;
28 | }
29 |
30 | @Override
31 | public void run(String... args) throws Exception {
32 | if (openExchangeRateConfig.isEnabled()) {
33 | String openExchangeRateUrlTemplate =
34 | openExchangeRateConfig.getSchema()
35 | + "://"
36 | + openExchangeRateConfig.getHost()
37 | + openExchangeRateConfig.getPath()
38 | + "?app_id={app_id}&symbols={symbols}";
39 |
40 | Map requestParams = Map.of(
41 | "app_id", openExchangeRateConfig.getAppId(),
42 | "symbols", String.join(",", openExchangeRateConfig.getSymbols())
43 | );
44 |
45 | ExchangeRatesDTO exchangeRatesDTO = restTemplate
46 | .getForObject(openExchangeRateUrlTemplate,
47 | ExchangeRatesDTO.class,
48 | requestParams
49 | );
50 |
51 | currencyService.refreshRates(exchangeRatesDTO);
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/mobilele/src/main/java/org/softuni/mobilele/model/dto/BrandDTO.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele.model.dto;
2 |
3 | import java.util.List;
4 |
5 | public record BrandDTO(String name, List models) {
6 | }
7 |
--------------------------------------------------------------------------------
/mobilele/src/main/java/org/softuni/mobilele/model/dto/ConvertRequestDTO.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele.model.dto;
2 |
3 | import jakarta.validation.constraints.NotEmpty;
4 | import jakarta.validation.constraints.NotNull;
5 | import jakarta.validation.constraints.Positive;
6 | import java.math.BigDecimal;
7 |
8 | public record ConvertRequestDTO(@NotEmpty String target, @NotNull @Positive BigDecimal amount) {
9 | }
10 |
--------------------------------------------------------------------------------
/mobilele/src/main/java/org/softuni/mobilele/model/dto/CreateOfferDTO.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele.model.dto;
2 |
3 | import jakarta.validation.constraints.Min;
4 | import jakarta.validation.constraints.NotEmpty;
5 | import jakarta.validation.constraints.NotNull;
6 | import jakarta.validation.constraints.Positive;
7 | import jakarta.validation.constraints.Size;
8 | import org.softuni.mobilele.model.enums.EngineEnum;
9 | import org.softuni.mobilele.model.enums.TransmissionEnum;
10 | import org.softuni.mobilele.model.validation.YearNotInTheFuture;
11 |
12 | public record CreateOfferDTO(@NotEmpty @Size(min = 5, max = 512) String description,
13 | @Positive @NotNull Long modelId, @NotNull EngineEnum engine,
14 | @NotNull TransmissionEnum transmission, @NotEmpty String imageUrl,
15 | @Positive @NotNull Integer mileage,
16 | @Positive @NotNull Integer price,
17 | @YearNotInTheFuture(message = "The year should not be in the future!")
18 | @NotNull(message = "Year must be provided!")
19 | @Min(1930)
20 | Integer year) {
21 |
22 | public static CreateOfferDTO empty() {
23 | return new CreateOfferDTO(null, null, null, null, null, null, null, null);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/mobilele/src/main/java/org/softuni/mobilele/model/dto/ExchangeRatesDTO.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele.model.dto;
2 |
3 | import java.math.BigDecimal;
4 | import java.util.Map;
5 |
6 | //{
7 | // "disclaimer": "Usage subject to terms: https://openexchangerates.org/terms",
8 | // "license": "https://openexchangerates.org/license",
9 | // "timestamp": 1698080400,
10 | // "base": "USD",
11 | // "rates": {
12 | // "BGN": 1.840515,
13 | // "EUR": 0.937668
14 | // }
15 | // }
16 | public record ExchangeRatesDTO(String base, Map rates) {
17 | }
18 |
--------------------------------------------------------------------------------
/mobilele/src/main/java/org/softuni/mobilele/model/dto/ModelDTO.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele.model.dto;
2 |
3 | public record ModelDTO(long id, String name) {
4 |
5 | }
6 |
--------------------------------------------------------------------------------
/mobilele/src/main/java/org/softuni/mobilele/model/dto/MoneyDTO.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele.model.dto;
2 |
3 | import java.math.BigDecimal;
4 |
5 | public record MoneyDTO(String currency, BigDecimal amount) {
6 | }
7 |
--------------------------------------------------------------------------------
/mobilele/src/main/java/org/softuni/mobilele/model/dto/OfferDetailDTO.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele.model.dto;
2 |
3 | import java.math.BigDecimal;
4 | import org.softuni.mobilele.model.enums.EngineEnum;
5 | import org.softuni.mobilele.model.enums.TransmissionEnum;
6 |
7 | public record OfferDetailDTO(
8 | String id,
9 | String brand,
10 | String model,
11 | int year,
12 | int mileage,
13 | BigDecimal price,
14 | EngineEnum engine,
15 | TransmissionEnum transmission,
16 | String imageUrl,
17 | String seller,
18 | boolean viewerIsOwner
19 | ) {
20 | public String summary() {
21 | return brand + " " + model + ", " + year;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/mobilele/src/main/java/org/softuni/mobilele/model/dto/OfferSummaryDTO.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele.model.dto;
2 |
3 | import java.math.BigDecimal;
4 | import org.softuni.mobilele.model.enums.EngineEnum;
5 | import org.softuni.mobilele.model.enums.TransmissionEnum;
6 |
7 | public record OfferSummaryDTO(
8 | String id,
9 | String brand,
10 | String model,
11 | int year,
12 | int mileage,
13 | BigDecimal price,
14 | EngineEnum engine,
15 | TransmissionEnum transmission,
16 | String imageUrl
17 | ) {
18 | public String summary() {
19 | return brand + " " + model + ", " + year;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/mobilele/src/main/java/org/softuni/mobilele/model/dto/ReCaptchaResponseDTO.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele.model.dto;
2 |
3 | import com.fasterxml.jackson.annotation.JsonInclude;
4 | import com.fasterxml.jackson.annotation.JsonInclude.Include;
5 | import java.util.List;
6 |
7 | @JsonInclude(Include.NON_NULL)
8 | public class ReCaptchaResponseDTO {
9 |
10 | private boolean success;
11 | private List errorCodes;
12 |
13 | public boolean isSuccess() {
14 | return success;
15 | }
16 |
17 | public ReCaptchaResponseDTO setSuccess(boolean success) {
18 | this.success = success;
19 | return this;
20 | }
21 |
22 | public List getErrorCodes() {
23 | return errorCodes;
24 | }
25 |
26 | public ReCaptchaResponseDTO setErrorCodes(List errorCodes) {
27 | this.errorCodes = errorCodes;
28 | return this;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/mobilele/src/main/java/org/softuni/mobilele/model/dto/UserRegistrationDTO.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele.model.dto;
2 |
3 | import jakarta.validation.constraints.Email;
4 | import jakarta.validation.constraints.NotEmpty;
5 | import jakarta.validation.constraints.NotNull;
6 | import org.softuni.mobilele.model.validation.FieldMatch;
7 | import org.softuni.mobilele.model.validation.UniqueUserEmail;
8 |
9 | @FieldMatch(
10 | first = "password",
11 | second = "confirmPassword",
12 | message = "Passwords should match."
13 | )
14 | public record UserRegistrationDTO(@NotEmpty String firstName,
15 | @NotEmpty String lastName,
16 | @NotNull @Email @UniqueUserEmail String email,
17 | String password,
18 | String confirmPassword) {
19 |
20 | public String fullName() {
21 | return firstName + " " + lastName;
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/mobilele/src/main/java/org/softuni/mobilele/model/entity/BaseEntity.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele.model.entity;
2 |
3 | import jakarta.persistence.GeneratedValue;
4 | import jakarta.persistence.GenerationType;
5 | import jakarta.persistence.Id;
6 | import jakarta.persistence.MappedSuperclass;
7 |
8 | @MappedSuperclass
9 | public class BaseEntity {
10 | @Id
11 | @GeneratedValue(strategy = GenerationType.IDENTITY)
12 | private Long id;
13 |
14 | public Long getId() {
15 | return id;
16 | }
17 |
18 | public BaseEntity setId(Long id) {
19 | this.id = id;
20 | return this;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/mobilele/src/main/java/org/softuni/mobilele/model/entity/BrandEntity.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele.model.entity;
2 |
3 | import jakarta.persistence.CascadeType;
4 | import jakarta.persistence.Column;
5 | import jakarta.persistence.Entity;
6 | import jakarta.persistence.FetchType;
7 | import jakarta.persistence.NamedAttributeNode;
8 | import jakarta.persistence.NamedEntityGraph;
9 | import jakarta.persistence.OneToMany;
10 | import jakarta.persistence.Table;
11 | import java.util.List;
12 |
13 | @Entity
14 | @Table(name = "brands")
15 | @NamedEntityGraph(
16 | name = "brandWithModels",
17 | attributeNodes = @NamedAttributeNode("models")
18 | )
19 | public class BrandEntity extends BaseEntity {
20 | @Column(unique = true, nullable = false)
21 | private String name;
22 |
23 | @OneToMany(
24 | fetch = FetchType.LAZY,
25 | cascade = CascadeType.ALL,
26 | mappedBy = "brand"
27 | )
28 | private List models;
29 |
30 | public BrandEntity setName(String name) {
31 | this.name = name;
32 | return this;
33 | }
34 |
35 | public String getName() {
36 | return name;
37 | }
38 |
39 | public List getModels() {
40 | return models;
41 | }
42 |
43 | public BrandEntity setModels(List models) {
44 | this.models = models;
45 | return this;
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/mobilele/src/main/java/org/softuni/mobilele/model/entity/ExchangeRateEntity.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele.model.entity;
2 |
3 | import jakarta.persistence.Entity;
4 | import jakarta.persistence.Id;
5 | import jakarta.persistence.Table;
6 | import jakarta.validation.constraints.NotNull;
7 | import java.math.BigDecimal;
8 |
9 | @Entity
10 | @Table(name = "exchange_rates")
11 | public class ExchangeRateEntity {
12 |
13 | @Id
14 | @NotNull
15 | private String currency;
16 |
17 | @NotNull
18 | private BigDecimal rate;
19 |
20 | public String getCurrency() {
21 | return currency;
22 | }
23 |
24 | public ExchangeRateEntity setCurrency(String currency) {
25 | this.currency = currency;
26 | return this;
27 | }
28 |
29 | public BigDecimal getRate() {
30 | return rate;
31 | }
32 |
33 | public ExchangeRateEntity setRate(BigDecimal rate) {
34 | this.rate = rate;
35 | return this;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/mobilele/src/main/java/org/softuni/mobilele/model/entity/ModelEntity.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele.model.entity;
2 |
3 | import jakarta.persistence.Entity;
4 | import jakarta.persistence.EnumType;
5 | import jakarta.persistence.Enumerated;
6 | import jakarta.persistence.ManyToOne;
7 | import jakarta.persistence.Table;
8 | import org.softuni.mobilele.model.enums.ModelCategoryEnum;
9 |
10 | @Entity
11 | @Table(name="models")
12 | public class ModelEntity extends BaseEntity {
13 |
14 | private String name;
15 |
16 | @Enumerated(EnumType.STRING)
17 | private ModelCategoryEnum category;
18 |
19 | @ManyToOne
20 | private BrandEntity brand;
21 |
22 | public String getName() {
23 | return name;
24 | }
25 |
26 | public ModelEntity setName(String name) {
27 | this.name = name;
28 | return this;
29 | }
30 |
31 | public ModelCategoryEnum getCategory() {
32 | return category;
33 | }
34 |
35 | public ModelEntity setCategory(ModelCategoryEnum category) {
36 | this.category = category;
37 | return this;
38 | }
39 |
40 | public BrandEntity getBrand() {
41 | return brand;
42 | }
43 |
44 | public ModelEntity setBrand(BrandEntity brand) {
45 | this.brand = brand;
46 | return this;
47 | }
48 |
49 | @Override
50 | public String toString() {
51 | return "ModelEntity{" +
52 | "name='" + name + '\'' +
53 | ", category=" + category +
54 | ", brand=" + brand +
55 | '}';
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/mobilele/src/main/java/org/softuni/mobilele/model/entity/UserActivationCodeEntity.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele.model.entity;
2 |
3 | import jakarta.persistence.Entity;
4 | import jakarta.persistence.ManyToOne;
5 | import jakarta.persistence.Table;
6 | import java.time.Instant;
7 | import java.time.LocalDateTime;
8 |
9 | @Entity
10 | @Table(name = "user_activation_codes")
11 | public class UserActivationCodeEntity extends BaseEntity {
12 |
13 | private String activationCode;
14 |
15 | private Instant created;
16 |
17 | @ManyToOne
18 | private UserEntity user;
19 |
20 | public String getActivationCode() {
21 | return activationCode;
22 | }
23 |
24 | public UserActivationCodeEntity setActivationCode(String activationCode) {
25 | this.activationCode = activationCode;
26 | return this;
27 | }
28 |
29 | public Instant getCreated() {
30 | return created;
31 | }
32 |
33 | public UserActivationCodeEntity setCreated(Instant created) {
34 | this.created = created;
35 | return this;
36 | }
37 |
38 | public UserEntity getUser() {
39 | return user;
40 | }
41 |
42 | public UserActivationCodeEntity setUser(UserEntity user) {
43 | this.user = user;
44 | return this;
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/mobilele/src/main/java/org/softuni/mobilele/model/entity/UserEntity.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele.model.entity;
2 |
3 | import jakarta.persistence.Column;
4 | import jakarta.persistence.Entity;
5 | import jakarta.persistence.FetchType;
6 | import jakarta.persistence.GeneratedValue;
7 | import jakarta.persistence.GenerationType;
8 | import jakarta.persistence.Id;
9 | import jakarta.persistence.JoinColumn;
10 | import jakarta.persistence.JoinTable;
11 | import jakarta.persistence.ManyToMany;
12 | import jakarta.persistence.Table;
13 | import java.util.ArrayList;
14 | import java.util.List;
15 |
16 | @Entity
17 | @Table(name = "users")
18 | public class UserEntity extends BaseEntity{
19 |
20 | @Column(unique = true)
21 | private String email;
22 |
23 | @ManyToMany(fetch = FetchType.EAGER)
24 | @JoinTable(
25 | name = "users_roles",
26 | joinColumns = @JoinColumn(name = "user_id"),
27 | inverseJoinColumns = @JoinColumn(name = "role_id"))
28 | private List roles = new ArrayList<>();
29 |
30 | private String password;
31 |
32 | private String firstName;
33 |
34 | private String lastName;
35 |
36 | private boolean active;
37 |
38 | public String getEmail() {
39 | return email;
40 | }
41 |
42 | public UserEntity setEmail(String email) {
43 | this.email = email;
44 | return this;
45 | }
46 |
47 | public String getPassword() {
48 | return password;
49 | }
50 |
51 | public UserEntity setPassword(String password) {
52 | this.password = password;
53 | return this;
54 | }
55 |
56 | public String getFirstName() {
57 | return firstName;
58 | }
59 |
60 | public UserEntity setFirstName(String firstName) {
61 | this.firstName = firstName;
62 | return this;
63 | }
64 |
65 | public String getLastName() {
66 | return lastName;
67 | }
68 |
69 | public UserEntity setLastName(String lastName) {
70 | this.lastName = lastName;
71 | return this;
72 | }
73 |
74 | public boolean isActive() {
75 | return active;
76 | }
77 |
78 | public UserEntity setActive(boolean active) {
79 | this.active = active;
80 | return this;
81 | }
82 |
83 | public List getRoles() {
84 | return roles;
85 | }
86 |
87 | public UserEntity setRoles(List roles) {
88 | this.roles = roles;
89 | return this;
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/mobilele/src/main/java/org/softuni/mobilele/model/entity/UserRoleEntity.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele.model.entity;
2 |
3 | import jakarta.persistence.Entity;
4 | import jakarta.persistence.EnumType;
5 | import jakarta.persistence.Enumerated;
6 | import jakarta.persistence.GeneratedValue;
7 | import jakarta.persistence.GenerationType;
8 | import jakarta.persistence.Id;
9 | import jakarta.persistence.Table;
10 | import org.softuni.mobilele.model.enums.UserRoleEnum;
11 |
12 | @Table(name = "roles")
13 | @Entity
14 | public class UserRoleEntity {
15 |
16 | @Id
17 | @GeneratedValue(strategy = GenerationType.IDENTITY)
18 | private Long id;
19 |
20 | @Enumerated(EnumType.STRING)
21 | private UserRoleEnum role;
22 |
23 | public Long getId() {
24 | return id;
25 | }
26 |
27 | public UserRoleEntity setId(Long id) {
28 | this.id = id;
29 | return this;
30 | }
31 |
32 | public UserRoleEnum getRole() {
33 | return role;
34 | }
35 |
36 | public UserRoleEntity setRole(UserRoleEnum role) {
37 | this.role = role;
38 | return this;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/mobilele/src/main/java/org/softuni/mobilele/model/enums/EngineEnum.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele.model.enums;
2 |
3 | public enum EngineEnum {
4 | PETROL,
5 | DIESEL,
6 | ELECTRIC;
7 | }
8 |
--------------------------------------------------------------------------------
/mobilele/src/main/java/org/softuni/mobilele/model/enums/ModelCategoryEnum.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele.model.enums;
2 |
3 | public enum ModelCategoryEnum {
4 | CAR,
5 | TRUCK,
6 | MOTORCYCLE;
7 | }
8 |
--------------------------------------------------------------------------------
/mobilele/src/main/java/org/softuni/mobilele/model/enums/TransmissionEnum.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele.model.enums;
2 |
3 | public enum TransmissionEnum {
4 | MANUAL,
5 | AUTOMATIC;
6 | }
7 |
--------------------------------------------------------------------------------
/mobilele/src/main/java/org/softuni/mobilele/model/enums/UserRoleEnum.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele.model.enums;
2 |
3 | public enum UserRoleEnum {
4 | ADMIN,
5 | USER;
6 | }
7 |
--------------------------------------------------------------------------------
/mobilele/src/main/java/org/softuni/mobilele/model/events/UserRegisteredEvent.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele.model.events;
2 |
3 | import org.springframework.context.ApplicationEvent;
4 |
5 | public class UserRegisteredEvent extends ApplicationEvent {
6 |
7 | private final String userEmail;
8 | private final String userNames;
9 |
10 | public UserRegisteredEvent(Object source,
11 | String userEmail,
12 | String userNames) {
13 | super(source);
14 | this.userEmail = userEmail;
15 | this.userNames = userNames;
16 | }
17 |
18 | public String getUserEmail() {
19 | return userEmail;
20 | }
21 |
22 | public String getUserNames() {
23 | return userNames;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/mobilele/src/main/java/org/softuni/mobilele/model/validation/FieldMatch.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele.model.validation;
2 |
3 | import jakarta.validation.Constraint;
4 | import jakarta.validation.Payload;
5 | import java.lang.annotation.ElementType;
6 | import java.lang.annotation.Retention;
7 | import java.lang.annotation.RetentionPolicy;
8 | import java.lang.annotation.Target;
9 |
10 | @Retention(RetentionPolicy.RUNTIME)
11 | @Target(ElementType.TYPE)
12 | @Constraint(validatedBy = UniqueUserEmailValidator.class)//todo
13 | public @interface FieldMatch {
14 |
15 | String first();
16 |
17 | String second();
18 |
19 | String message() default "Fields should match";
20 |
21 | Class>[] groups() default {};
22 |
23 | Class extends Payload>[] payload() default {};
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/mobilele/src/main/java/org/softuni/mobilele/model/validation/FieldMatchValidator.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele.model.validation;
2 |
3 | import jakarta.validation.ConstraintValidator;
4 | import jakarta.validation.ConstraintValidatorContext;
5 | import java.util.Objects;
6 | import org.springframework.beans.BeanWrapper;
7 | import org.springframework.beans.PropertyAccessException;
8 | import org.springframework.beans.PropertyAccessorFactory;
9 |
10 | public class FieldMatchValidator implements ConstraintValidator {
11 |
12 | private String first;
13 | private String second;
14 | private String message;
15 |
16 | @Override
17 | public void initialize(FieldMatch constraintAnnotation) {
18 | this.first = constraintAnnotation.first();
19 | this.second = constraintAnnotation.second();
20 | this.message = constraintAnnotation.message();
21 | }
22 |
23 | @Override
24 | public boolean isValid(Object value, ConstraintValidatorContext context) {
25 | BeanWrapper beanWrapper = PropertyAccessorFactory
26 | .forBeanPropertyAccess(value);
27 |
28 | Object firstProperty = beanWrapper.getPropertyValue(this.first);
29 | Object secondProperty = beanWrapper.getPropertyValue(this.second);
30 |
31 | boolean isValid = Objects.equals(firstProperty, secondProperty);
32 |
33 | if (!isValid) {
34 | context
35 | .buildConstraintViolationWithTemplate(message)
36 | .addPropertyNode(second)
37 | .addConstraintViolation()
38 | .disableDefaultConstraintViolation();
39 | }
40 |
41 | return isValid;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/mobilele/src/main/java/org/softuni/mobilele/model/validation/UniqueUserEmail.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele.model.validation;
2 |
3 | import jakarta.validation.Constraint;
4 | import jakarta.validation.Payload;
5 | import java.lang.annotation.ElementType;
6 | import java.lang.annotation.Retention;
7 | import java.lang.annotation.RetentionPolicy;
8 | import java.lang.annotation.Target;
9 |
10 | @Retention(RetentionPolicy.RUNTIME)
11 | @Target(ElementType.FIELD)
12 | @Constraint(validatedBy = UniqueUserEmailValidator.class)
13 | public @interface UniqueUserEmail {
14 |
15 | String message() default "The user email should be unique";
16 |
17 | Class>[] groups() default {};
18 |
19 | Class extends Payload>[] payload() default {};
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/mobilele/src/main/java/org/softuni/mobilele/model/validation/UniqueUserEmailValidator.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele.model.validation;
2 |
3 | import jakarta.validation.ConstraintValidator;
4 | import jakarta.validation.ConstraintValidatorContext;
5 | import org.softuni.mobilele.repository.UserRepository;
6 |
7 | public class UniqueUserEmailValidator implements ConstraintValidator {
8 |
9 | private final UserRepository userRepository;
10 |
11 | public UniqueUserEmailValidator(UserRepository userRepository) {
12 | this.userRepository = userRepository;
13 | }
14 |
15 | @Override
16 | public boolean isValid(String value, ConstraintValidatorContext context) {
17 | return userRepository
18 | .findByEmail(value)
19 | .isEmpty();
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/mobilele/src/main/java/org/softuni/mobilele/model/validation/YearNotInTheFuture.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele.model.validation;
2 |
3 | import jakarta.validation.Constraint;
4 | import jakarta.validation.Payload;
5 | import java.lang.annotation.ElementType;
6 | import java.lang.annotation.Retention;
7 | import java.lang.annotation.RetentionPolicy;
8 | import java.lang.annotation.Target;
9 |
10 | @Retention(RetentionPolicy.RUNTIME)
11 | @Target(ElementType.FIELD)
12 | @Constraint(validatedBy = YearNotInTheFutureValidator.class)
13 | public @interface YearNotInTheFuture {
14 |
15 | String message() default "must not be in the future";
16 |
17 | Class>[] groups() default {};
18 |
19 | Class extends Payload>[] payload() default {};
20 |
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/mobilele/src/main/java/org/softuni/mobilele/model/validation/YearNotInTheFutureValidator.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele.model.validation;
2 |
3 | import jakarta.validation.Constraint;
4 | import jakarta.validation.ConstraintValidator;
5 | import jakarta.validation.ConstraintValidatorContext;
6 | import java.time.Year;
7 |
8 | public class YearNotInTheFutureValidator implements ConstraintValidator {
9 |
10 | @Override
11 | public boolean isValid(Number value, ConstraintValidatorContext context) {
12 |
13 | if (value == null) {
14 | return true;
15 | } else {
16 |
17 | int currentYear = Year.now().getValue();
18 |
19 | return value.intValue() <= currentYear;
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/mobilele/src/main/java/org/softuni/mobilele/repository/BrandRepository.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele.repository;
2 |
3 | import java.util.List;
4 | import org.softuni.mobilele.model.entity.BrandEntity;
5 | import org.softuni.mobilele.model.entity.OfferEntity;
6 | import org.springframework.data.jpa.repository.EntityGraph;
7 | import org.springframework.data.jpa.repository.JpaRepository;
8 | import org.springframework.data.jpa.repository.Query;
9 | import org.springframework.stereotype.Repository;
10 |
11 | @Repository
12 | public interface BrandRepository extends JpaRepository {
13 |
14 | // @Query("SELECT b FROM BrandEntity b "
15 | // + "JOIN FETCH b.models ")
16 | @EntityGraph(
17 | value = "brandWithModels",
18 | attributePaths = "models"
19 | )
20 | @Query("SELECT b FROM BrandEntity b")
21 | List getAllBrands();
22 |
23 | }
--------------------------------------------------------------------------------
/mobilele/src/main/java/org/softuni/mobilele/repository/ExchangeRateRepository.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele.repository;
2 |
3 | import org.softuni.mobilele.model.entity.ExchangeRateEntity;
4 | import org.springframework.data.jpa.repository.JpaRepository;
5 |
6 | public interface ExchangeRateRepository extends JpaRepository {
7 | }
8 |
--------------------------------------------------------------------------------
/mobilele/src/main/java/org/softuni/mobilele/repository/ModelRepository.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele.repository;
2 |
3 | import org.softuni.mobilele.model.entity.ModelEntity;
4 | import org.springframework.data.jpa.repository.JpaRepository;
5 | import org.springframework.stereotype.Repository;
6 |
7 | import java.util.List;
8 |
9 | @Repository
10 | public interface ModelRepository extends JpaRepository {
11 |
12 |
13 | List findAllByBrandId (Long id);
14 | }
15 |
--------------------------------------------------------------------------------
/mobilele/src/main/java/org/softuni/mobilele/repository/OfferRepository.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele.repository;
2 |
3 | import java.util.Optional;
4 | import java.util.UUID;
5 | import org.softuni.mobilele.model.entity.OfferEntity;
6 | import org.springframework.data.jpa.repository.JpaRepository;
7 | import org.springframework.stereotype.Repository;
8 |
9 | @Repository
10 | public interface OfferRepository extends JpaRepository {
11 | Optional findByUuid(UUID uuid);
12 |
13 | void deleteByUuid(UUID uuid);
14 | }
15 |
--------------------------------------------------------------------------------
/mobilele/src/main/java/org/softuni/mobilele/repository/UserActivationCodeRepository.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele.repository;
2 |
3 | import org.softuni.mobilele.model.entity.UserActivationCodeEntity;
4 | import org.springframework.data.jpa.repository.JpaRepository;
5 | import org.springframework.stereotype.Repository;
6 |
7 | @Repository
8 | public interface UserActivationCodeRepository extends JpaRepository {
9 |
10 | }
11 |
--------------------------------------------------------------------------------
/mobilele/src/main/java/org/softuni/mobilele/repository/UserRepository.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele.repository;
2 |
3 | import java.util.Optional;
4 | import org.softuni.mobilele.model.entity.UserEntity;
5 | import org.springframework.data.jpa.repository.JpaRepository;
6 | import org.springframework.stereotype.Repository;
7 | @Repository
8 | public interface UserRepository extends JpaRepository {
9 | Optional findByEmail(String email);
10 | }
11 |
--------------------------------------------------------------------------------
/mobilele/src/main/java/org/softuni/mobilele/repository/UserRoleRepository.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele.repository;
2 |
3 | import java.util.List;
4 | import org.softuni.mobilele.model.entity.UserRoleEntity;
5 | import org.softuni.mobilele.model.enums.UserRoleEnum;
6 | import org.springframework.data.jpa.repository.JpaRepository;
7 |
8 | public interface UserRoleRepository extends JpaRepository {
9 |
10 | List findAllByRoleIn(List roles);
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/mobilele/src/main/java/org/softuni/mobilele/service/BrandService.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele.service;
2 |
3 | import java.util.List;
4 | import org.softuni.mobilele.model.dto.BrandDTO;
5 |
6 | public interface BrandService {
7 |
8 | List getAllBrands();
9 |
10 | }
11 |
--------------------------------------------------------------------------------
/mobilele/src/main/java/org/softuni/mobilele/service/CurrencyService.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele.service;
2 |
3 | import org.softuni.mobilele.model.dto.ConvertRequestDTO;
4 | import org.softuni.mobilele.model.dto.ExchangeRatesDTO;
5 | import org.softuni.mobilele.model.dto.MoneyDTO;
6 |
7 | public interface CurrencyService {
8 |
9 | void refreshRates(ExchangeRatesDTO exchangeRatesDTO);
10 |
11 | MoneyDTO convert(ConvertRequestDTO convertRequestDTO);
12 | }
13 |
--------------------------------------------------------------------------------
/mobilele/src/main/java/org/softuni/mobilele/service/EmailService.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele.service;
2 |
3 | public interface EmailService {
4 | void sendRegistrationEmail(
5 | String userEmail,
6 | String userName,
7 | String activationCode);
8 | }
9 |
--------------------------------------------------------------------------------
/mobilele/src/main/java/org/softuni/mobilele/service/MonitoringService.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele.service;
2 |
3 | public interface MonitoringService {
4 | void logOfferSearch();
5 | }
6 |
--------------------------------------------------------------------------------
/mobilele/src/main/java/org/softuni/mobilele/service/OfferService.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele.service;
2 |
3 |
4 | import java.util.Optional;
5 | import java.util.UUID;
6 | import org.softuni.mobilele.model.dto.CreateOfferDTO;
7 | import org.softuni.mobilele.model.dto.OfferDetailDTO;
8 | import org.softuni.mobilele.model.dto.OfferSummaryDTO;
9 | import org.springframework.data.domain.Page;
10 | import org.springframework.data.domain.Pageable;
11 | import org.springframework.security.core.userdetails.UserDetails;
12 |
13 | public interface OfferService {
14 |
15 | UUID createOffer(CreateOfferDTO createOfferDTO, UserDetails seller);
16 |
17 | Page getAllOffers(Pageable pageable);
18 |
19 | Optional getOfferDetail(UUID offerUUID, UserDetails viewer);
20 |
21 | void deleteOffer(UUID offerUUID);
22 |
23 | boolean isOwner(UUID uuid, String userName);
24 | }
25 |
--------------------------------------------------------------------------------
/mobilele/src/main/java/org/softuni/mobilele/service/ReCaptchaService.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele.service;
2 |
3 | import java.util.Optional;
4 | import org.softuni.mobilele.model.dto.ReCaptchaResponseDTO;
5 |
6 | public interface ReCaptchaService {
7 | Optional verify(String token);
8 | }
9 |
--------------------------------------------------------------------------------
/mobilele/src/main/java/org/softuni/mobilele/service/UserActivationService.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele.service;
2 |
3 | import org.softuni.mobilele.model.events.UserRegisteredEvent;
4 |
5 | public interface UserActivationService {
6 |
7 | void userRegistered(UserRegisteredEvent event);
8 |
9 | void cleanUpObsoleteActivationLinks();
10 |
11 | String createActivationCode(String userEmail);
12 | }
13 |
--------------------------------------------------------------------------------
/mobilele/src/main/java/org/softuni/mobilele/service/UserService.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele.service;
2 |
3 | import org.softuni.mobilele.model.dto.UserRegistrationDTO;
4 | import org.springframework.security.core.Authentication;
5 |
6 | public interface UserService {
7 |
8 | void registerUser(UserRegistrationDTO userRegistrationDTO);
9 |
10 | void createUserIfNotExist(String email, String names);
11 |
12 | Authentication login(String email);
13 | }
14 |
--------------------------------------------------------------------------------
/mobilele/src/main/java/org/softuni/mobilele/service/aop/MonitoringAspect.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele.service.aop;
2 |
3 | import java.lang.reflect.Method;
4 | import org.aspectj.lang.ProceedingJoinPoint;
5 | import org.aspectj.lang.annotation.Around;
6 | import org.aspectj.lang.annotation.Aspect;
7 | import org.aspectj.lang.annotation.Before;
8 | import org.aspectj.lang.reflect.MethodSignature;
9 | import org.slf4j.Logger;
10 | import org.slf4j.LoggerFactory;
11 | import org.softuni.mobilele.service.MonitoringService;
12 | import org.springframework.stereotype.Component;
13 | import org.springframework.util.StopWatch;
14 |
15 | @Aspect
16 | @Component
17 | public class MonitoringAspect {
18 |
19 | private static final Logger LOGGER = LoggerFactory.getLogger(MonitoringAspect.class);
20 | private final MonitoringService monitoringService;
21 |
22 | public MonitoringAspect(MonitoringService monitoringService) {
23 | this.monitoringService = monitoringService;
24 | }
25 |
26 | @Before("Pointcuts.trackOfferSearch()")
27 | public void logOfferSearch() {
28 | monitoringService.logOfferSearch();
29 | }
30 |
31 | @Around("Pointcuts.warnIfExecutionExceeds()")
32 | public Object logExecutionTime(ProceedingJoinPoint pjp) throws Throwable {
33 |
34 | WarnIfExecutionExceeds annotation = getAnnotation(pjp);
35 | long timeout = annotation.timeInMillis();
36 |
37 | StopWatch stopWatch = new StopWatch();
38 | stopWatch.start();
39 |
40 | var returnValue = pjp.proceed();
41 |
42 | stopWatch.stop();
43 |
44 | if (stopWatch.getLastTaskTimeMillis() > timeout) {
45 | LOGGER.warn("The method {} ran for {} millis which is more than the " +
46 | "expected {} millis.",
47 | pjp.getSignature(),
48 | stopWatch.getLastTaskTimeMillis(),
49 | timeout
50 | );
51 | }
52 |
53 | return returnValue;
54 | }
55 |
56 | private static WarnIfExecutionExceeds getAnnotation(ProceedingJoinPoint pjp) {
57 |
58 | Method method = ((MethodSignature)pjp.getSignature()).getMethod();
59 |
60 | try {
61 | return pjp
62 | .getTarget()
63 | .getClass()
64 | .getMethod(method.getName(), method.getParameterTypes())
65 | .getAnnotation(WarnIfExecutionExceeds.class);
66 | } catch (NoSuchMethodException e) {
67 | throw new RuntimeException(e);
68 | }
69 | }
70 |
71 |
72 | }
73 |
--------------------------------------------------------------------------------
/mobilele/src/main/java/org/softuni/mobilele/service/aop/Pointcuts.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele.service.aop;
2 |
3 | import org.aspectj.lang.annotation.Pointcut;
4 |
5 | public class Pointcuts {
6 | @Pointcut("execution(* org.softuni.mobilele.service.OfferService.getAllOffers(..))")
7 | public void trackOfferSearch(){}
8 |
9 | @Pointcut("@annotation(WarnIfExecutionExceeds)")
10 | public void warnIfExecutionExceeds(){}
11 | }
12 |
--------------------------------------------------------------------------------
/mobilele/src/main/java/org/softuni/mobilele/service/aop/WarnIfExecutionExceeds.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele.service.aop;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | @Target(ElementType.METHOD)
9 | @Retention(RetentionPolicy.RUNTIME)
10 | public @interface WarnIfExecutionExceeds {
11 | long timeInMillis();
12 | }
13 |
--------------------------------------------------------------------------------
/mobilele/src/main/java/org/softuni/mobilele/service/exception/ObjectNotFoundException.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele.service.exception;
2 |
3 | import org.springframework.http.HttpStatus;
4 | import org.springframework.web.bind.annotation.ResponseStatus;
5 |
6 | @ResponseStatus(HttpStatus.NOT_FOUND)
7 | public class ObjectNotFoundException extends RuntimeException{
8 | public ObjectNotFoundException(String message) {
9 | super(message);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/mobilele/src/main/java/org/softuni/mobilele/service/impl/BrandServiceImpl.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele.service.impl;
2 |
3 | import java.util.*;
4 | import java.util.stream.Collectors;
5 |
6 | import org.softuni.mobilele.model.dto.BrandDTO;
7 | import org.softuni.mobilele.model.dto.ModelDTO;
8 | import org.softuni.mobilele.repository.BrandRepository;
9 | import org.softuni.mobilele.repository.ModelRepository;
10 | import org.softuni.mobilele.service.BrandService;
11 | import org.springframework.stereotype.Service;
12 |
13 | @Service
14 | public class BrandServiceImpl implements BrandService {
15 | private final BrandRepository brandRepository;
16 |
17 | public BrandServiceImpl(BrandRepository brandRepository) {
18 | this.brandRepository = brandRepository;
19 | }
20 |
21 | @Override
22 | public List getAllBrands() {
23 |
24 | return brandRepository.getAllBrands().stream()
25 | .map(brand -> new BrandDTO(
26 | brand.getName(),
27 | brand.getModels().stream()
28 | .map(model -> new ModelDTO(model.getId(), model.getName()))
29 | .sorted(Comparator.comparing(ModelDTO::name))
30 | .collect(Collectors.toList())
31 | ))
32 | .sorted(Comparator.comparing(BrandDTO::name))
33 | .collect(Collectors.toList());
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/mobilele/src/main/java/org/softuni/mobilele/service/impl/EmailServiceImpl.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele.service.impl;
2 |
3 | import jakarta.mail.MessagingException;
4 | import jakarta.mail.internet.MimeMessage;
5 | import org.softuni.mobilele.service.EmailService;
6 | import org.softuni.mobilele.service.aop.WarnIfExecutionExceeds;
7 | import org.springframework.beans.factory.annotation.Value;
8 | import org.springframework.mail.javamail.JavaMailSender;
9 | import org.springframework.mail.javamail.JavaMailSenderImpl;
10 | import org.springframework.mail.javamail.MimeMessageHelper;
11 | import org.springframework.stereotype.Service;
12 | import org.thymeleaf.TemplateEngine;
13 | import org.thymeleaf.context.Context;
14 |
15 | @Service
16 | public class EmailServiceImpl implements EmailService {
17 |
18 | private final TemplateEngine templateEngine;
19 | private final JavaMailSender javaMailSender;
20 | private final String mobileleEmail;
21 |
22 | public EmailServiceImpl(
23 | TemplateEngine templateEngine,
24 | JavaMailSender javaMailSender,
25 | @Value("${mail.mobilele}") String mobileleEmail) {
26 | this.templateEngine = templateEngine;
27 | this.javaMailSender = javaMailSender;
28 | this.mobileleEmail = mobileleEmail;
29 | }
30 |
31 | @Override
32 | @WarnIfExecutionExceeds(timeInMillis = 600)
33 | public void sendRegistrationEmail(String userEmail, String userName, String activationCode) {
34 | MimeMessage mimeMessage = javaMailSender.createMimeMessage();
35 |
36 | MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMessage);
37 |
38 | try {
39 |
40 | mimeMessageHelper.setTo(userEmail);
41 | mimeMessageHelper.setFrom(mobileleEmail);
42 | mimeMessageHelper.setReplyTo(mobileleEmail);
43 | mimeMessageHelper.setSubject("Welcome to mobilele!");
44 | mimeMessageHelper.setText(generateRegistrationEmailBody(userName, activationCode), true);
45 |
46 | javaMailSender.send(mimeMessageHelper.getMimeMessage());
47 |
48 | } catch (MessagingException e) {
49 | throw new RuntimeException(e);
50 | }
51 | }
52 |
53 | private String generateRegistrationEmailBody(String userName, String activationCode) {
54 |
55 | Context context = new Context();
56 | context.setVariable("username", userName);
57 | context.setVariable("activation_code", activationCode);
58 |
59 | return templateEngine.process("email/registration-email", context);
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/mobilele/src/main/java/org/softuni/mobilele/service/impl/MobileleUserDetailsService.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele.service.impl;
2 |
3 | import java.util.List;
4 | import org.softuni.mobilele.model.entity.UserEntity;
5 | import org.softuni.mobilele.model.entity.UserRoleEntity;
6 | import org.softuni.mobilele.repository.UserRepository;
7 | import org.springframework.security.core.GrantedAuthority;
8 | import org.springframework.security.core.authority.SimpleGrantedAuthority;
9 | import org.springframework.security.core.userdetails.User;
10 | import org.springframework.security.core.userdetails.UserDetails;
11 | import org.springframework.security.core.userdetails.UserDetailsService;
12 | import org.springframework.security.core.userdetails.UsernameNotFoundException;
13 |
14 | public class MobileleUserDetailsService implements UserDetailsService {
15 |
16 | private final UserRepository userRepository;
17 |
18 | public MobileleUserDetailsService(UserRepository userRepository){
19 | this.userRepository = userRepository;
20 | }
21 |
22 | @Override
23 | public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
24 | return userRepository
25 | .findByEmail(email)
26 | .map(MobileleUserDetailsService::map)
27 | .orElseThrow(() -> new UsernameNotFoundException("User " + email + " not found!"));
28 | }
29 |
30 | private static UserDetails map(UserEntity userEntity) {
31 | return User
32 | .withUsername(userEntity.getEmail())
33 | .password(userEntity.getPassword())
34 | .authorities(userEntity.getRoles().stream().map(MobileleUserDetailsService::map).toList())
35 | .build();
36 | }
37 |
38 | private static GrantedAuthority map(UserRoleEntity userRoleEntity) {
39 | return new SimpleGrantedAuthority(
40 | "ROLE_" + userRoleEntity.getRole().name()
41 | );
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/mobilele/src/main/java/org/softuni/mobilele/service/impl/MonitoringServiceImpl.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele.service.impl;
2 |
3 | import io.micrometer.core.instrument.Counter;
4 | import io.micrometer.core.instrument.MeterRegistry;
5 | import org.slf4j.Logger;
6 | import org.slf4j.LoggerFactory;
7 | import org.softuni.mobilele.service.MonitoringService;
8 | import org.springframework.stereotype.Service;
9 |
10 | @Service
11 | public class MonitoringServiceImpl implements MonitoringService {
12 |
13 | private Logger LOGGER = LoggerFactory.getLogger(MonitoringServiceImpl.class);
14 |
15 | private final Counter offerSearches;
16 |
17 | public MonitoringServiceImpl(MeterRegistry meterRegistry) {
18 | offerSearches = Counter
19 | .builder("offer_search_cnt")
20 | .description("How many offer searched we have performed")
21 | .register(meterRegistry);
22 | }
23 |
24 | @Override
25 | public void logOfferSearch() {
26 | LOGGER.info("Offer search was performed.");
27 | offerSearches.increment();
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/mobilele/src/main/java/org/softuni/mobilele/service/impl/ReCaptchaServiceImpl.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele.service.impl;
2 |
3 | import java.net.URI;
4 | import java.util.Optional;
5 | import org.slf4j.Logger;
6 | import org.slf4j.LoggerFactory;
7 | import org.softuni.mobilele.config.ReCaptchaConfig;
8 | import org.softuni.mobilele.model.dto.ReCaptchaResponseDTO;
9 | import org.softuni.mobilele.service.ReCaptchaService;
10 | import org.springframework.stereotype.Service;
11 | import org.springframework.web.reactive.function.BodyInserters;
12 | import org.springframework.web.reactive.function.client.WebClient;
13 | import org.springframework.web.util.UriBuilder;
14 |
15 | @Service
16 | public class ReCaptchaServiceImpl implements ReCaptchaService {
17 |
18 | private static final Logger LOGGER =
19 | LoggerFactory.getLogger(ReCaptchaServiceImpl.class);
20 | private final WebClient webClient;
21 | private final ReCaptchaConfig reCaptchaConfig;
22 |
23 | public ReCaptchaServiceImpl(
24 | WebClient webClient,
25 | ReCaptchaConfig reCaptchaConfig) {
26 | this.webClient = webClient;
27 | this.reCaptchaConfig = reCaptchaConfig;
28 | }
29 |
30 | @Override
31 | public Optional verify(String token) {
32 | ReCaptchaResponseDTO response = webClient
33 | .post()
34 | .uri(this::buildRecaptchaURI)
35 | .body(BodyInserters
36 | .fromFormData("secret", reCaptchaConfig.getSecret())
37 | .with("response", token))
38 | .retrieve()
39 | .bodyToMono(ReCaptchaResponseDTO.class)
40 | .doOnError(t -> LOGGER.error("Error fetching google reponse...", t))
41 | .onErrorComplete()
42 | .block();
43 |
44 | return Optional.ofNullable(response);
45 | }
46 |
47 | private URI buildRecaptchaURI(UriBuilder uriBuilder) {
48 | // REST endpoint for google verification.
49 | // https://www.google.com/recaptcha/api/siteverify
50 | return uriBuilder
51 | .scheme("https")
52 | .host("www.google.com")
53 | .path("/recaptcha/api/siteverify")
54 | .build();
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/mobilele/src/main/java/org/softuni/mobilele/service/oauth/OAuthSuccessHandler.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele.service.oauth;
2 |
3 | import jakarta.servlet.ServletException;
4 | import jakarta.servlet.http.HttpServletRequest;
5 | import jakarta.servlet.http.HttpServletResponse;
6 | import java.io.IOException;
7 | import org.softuni.mobilele.service.UserService;
8 | import org.springframework.security.core.Authentication;
9 | import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
10 | import org.springframework.security.oauth2.core.user.OAuth2User;
11 | import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
12 | import org.springframework.stereotype.Component;
13 |
14 | @Component
15 | public class OAuthSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
16 |
17 | private final UserService userService;
18 |
19 | public OAuthSuccessHandler(UserService userService) {
20 | this.userService = userService;
21 | }
22 |
23 | @Override
24 | public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
25 | Authentication authentication) throws ServletException, IOException {
26 |
27 | if (authentication instanceof OAuth2AuthenticationToken token) {
28 |
29 | OAuth2User user = token.getPrincipal();
30 |
31 | String email = user
32 | .getAttribute("email");
33 | String name = user
34 | .getAttribute("name");
35 |
36 | userService.createUserIfNotExist(email, name);
37 | authentication = userService.login(email);
38 | }
39 |
40 | super.onAuthenticationSuccess(request, response, authentication);
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/mobilele/src/main/java/org/softuni/mobilele/service/schedulers/ActivationLinkCleanupScheduler.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele.service.schedulers;
2 |
3 | import java.time.LocalDateTime;
4 | import org.softuni.mobilele.service.UserActivationService;
5 | import org.springframework.scheduling.annotation.Scheduled;
6 | import org.springframework.stereotype.Component;
7 |
8 | @Component
9 | public class ActivationLinkCleanupScheduler {
10 |
11 | private final UserActivationService userActivationService;
12 |
13 | public ActivationLinkCleanupScheduler(UserActivationService userActivationService) {
14 | this.userActivationService = userActivationService;
15 | }
16 |
17 | //@Scheduled(cron = "*/10 * * * * *")
18 | @Scheduled(fixedRate = 10_000)
19 | public void cleanUp() {
20 | //System.out.println("Trigger cleanup of activation links. " + LocalDateTime.now());
21 | // TODO.
22 | userActivationService.cleanUpObsoleteActivationLinks();
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/mobilele/src/main/java/org/softuni/mobilele/web/BrandController.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele.web;
2 |
3 |
4 | import org.springframework.stereotype.Controller;
5 | import org.springframework.web.bind.annotation.GetMapping;
6 |
7 | @Controller
8 | public class BrandController {
9 |
10 | @GetMapping("/brands")
11 | public String allBrands() {
12 | return "brands";
13 | }
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/mobilele/src/main/java/org/softuni/mobilele/web/CurrencyRestController.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele.web;
2 |
3 | import io.swagger.v3.oas.annotations.Operation;
4 | import io.swagger.v3.oas.annotations.Parameter;
5 | import io.swagger.v3.oas.annotations.media.Content;
6 | import io.swagger.v3.oas.annotations.media.Schema;
7 | import io.swagger.v3.oas.annotations.responses.ApiResponse;
8 | import io.swagger.v3.oas.annotations.responses.ApiResponses;
9 | import jakarta.validation.Valid;
10 | import org.softuni.mobilele.model.dto.ConvertRequestDTO;
11 | import org.softuni.mobilele.model.dto.MoneyDTO;
12 | import org.softuni.mobilele.service.CurrencyService;
13 | import org.springframework.web.bind.annotation.GetMapping;
14 | import org.springframework.web.bind.annotation.RestController;
15 |
16 | @RestController
17 | public class CurrencyRestController {
18 |
19 | private final CurrencyService currencyService;
20 |
21 | public CurrencyRestController(CurrencyService currencyService) {
22 | this.currencyService = currencyService;
23 | }
24 |
25 | @Operation(summary = "Converts BGN to a target currency.")
26 | @ApiResponses(value = {
27 | @ApiResponse(
28 | responseCode = "200",
29 | description = "Returned when we successfully converted the currency.",
30 | content = {
31 | @Content(
32 | mediaType = "application/json",
33 | schema = @Schema(implementation = MoneyDTO.class)
34 | )
35 | }
36 | ),
37 | @ApiResponse(
38 | responseCode = "404",
39 | description = "There is no information about this currency",
40 | content = @Content
41 | )
42 | })
43 | @Parameter(name = "target", description = "The targer currency", required = true)
44 | @Parameter(name = "amount", description = "The amount to be converted", required = true)
45 | @GetMapping("/api/currency/convert")
46 | public MoneyDTO convert(@Valid ConvertRequestDTO convertRequestDTO) {
47 | return currencyService.convert(convertRequestDTO);
48 | }
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/mobilele/src/main/java/org/softuni/mobilele/web/OffersController.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele.web;
2 |
3 | import org.softuni.mobilele.model.dto.OfferSummaryDTO;
4 | import org.softuni.mobilele.service.OfferService;
5 | import org.springframework.data.domain.Page;
6 | import org.springframework.data.domain.Pageable;
7 | import org.springframework.data.web.PageableDefault;
8 | import org.springframework.stereotype.Controller;
9 | import org.springframework.ui.Model;
10 | import org.springframework.web.bind.annotation.GetMapping;
11 | import org.springframework.web.bind.annotation.RequestMapping;
12 |
13 | @Controller
14 | @RequestMapping("/offers")
15 | public class OffersController {
16 |
17 | private final OfferService offerService;
18 |
19 | public OffersController(OfferService offerService) {
20 | this.offerService = offerService;
21 | }
22 |
23 | @GetMapping("/all")
24 | public String all(Model model,
25 | @PageableDefault(
26 | size = 3,
27 | sort = "uuid"
28 | ) Pageable pageable) {
29 |
30 | Page allOffers = offerService.getAllOffers(pageable);
31 |
32 | model.addAttribute("offers", allOffers);
33 |
34 | return "offers";
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/mobilele/src/main/java/org/softuni/mobilele/web/UserLoginController.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele.web;
2 |
3 | import org.springframework.stereotype.Controller;
4 | import org.springframework.ui.Model;
5 | import org.springframework.web.bind.annotation.GetMapping;
6 | import org.springframework.web.bind.annotation.ModelAttribute;
7 | import org.springframework.web.bind.annotation.PostMapping;
8 |
9 | @Controller
10 | public class UserLoginController {
11 |
12 | @GetMapping("/users/login")
13 | public String login() {
14 | return "auth-login";
15 | }
16 |
17 | @PostMapping("/users/login-error")
18 | public String onFailure(
19 | @ModelAttribute("email") String email,
20 | Model model) {
21 |
22 | model.addAttribute("email", email);
23 | model.addAttribute("bad_credentials", "true");
24 |
25 | return "auth-login";
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/mobilele/src/main/java/org/softuni/mobilele/web/UserRegistrationController.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele.web;
2 |
3 | import org.softuni.mobilele.model.dto.ReCaptchaResponseDTO;
4 | import org.softuni.mobilele.model.dto.UserRegistrationDTO;
5 | import org.softuni.mobilele.service.ReCaptchaService;
6 | import org.softuni.mobilele.service.UserService;
7 | import org.springframework.beans.factory.annotation.Autowired;
8 | import org.springframework.stereotype.Controller;
9 | import org.springframework.web.bind.annotation.GetMapping;
10 | import org.springframework.web.bind.annotation.PostMapping;
11 | import org.springframework.web.bind.annotation.RequestBody;
12 | import org.springframework.web.bind.annotation.RequestMapping;
13 | import org.springframework.web.bind.annotation.RequestParam;
14 |
15 | @RequestMapping("/users")
16 | @Controller
17 | public class UserRegistrationController {
18 |
19 | private final UserService userService;
20 | private final ReCaptchaService reCaptchaService;
21 |
22 | public UserRegistrationController(
23 | UserService userService,
24 | ReCaptchaService reCaptchaService) {
25 | this.userService = userService;
26 | this.reCaptchaService = reCaptchaService;
27 | }
28 |
29 | @GetMapping("/register")
30 | public String register() {
31 | return "auth-register";
32 | }
33 |
34 | @PostMapping("/register")
35 | public String register(UserRegistrationDTO userRegistrationDTO,
36 | @RequestParam("g-recaptcha-response") String reCaptchaResponse) {
37 |
38 | boolean isBot = !reCaptchaService
39 | .verify(reCaptchaResponse)
40 | .map(ReCaptchaResponseDTO::isSuccess)
41 | .orElse(false);
42 |
43 | if (isBot) {
44 | return "redirect:/";
45 | }
46 |
47 | userService.registerUser(userRegistrationDTO);
48 |
49 | return "redirect:/";
50 | }
51 |
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/mobilele/src/main/resources/application.yaml:
--------------------------------------------------------------------------------
1 | spring:
2 | datasource:
3 | driverClassName: com.mysql.cj.jdbc.Driver
4 | url: jdbc:mysql://${MYSQL_HOST:localhost}:3306/mobilele?allowPublicKeyRetrieval=true&useSSL=false&createDatabaseIfNotExist=true&serverTimezone=UTC
5 | username: ${MYSQL_USER:root}
6 | password: ${MYSQL_PASSWORD:}
7 | jpa:
8 | database-platform: org.hibernate.dialect.MySQLDialect
9 | defer-datasource-initialization: true
10 | properties:
11 | hibernate:
12 | format_sql: true
13 | hibernate:
14 | ddl-auto: update
15 | sql:
16 | init:
17 | mode: never
18 | security:
19 | oauth2:
20 | client:
21 | registration:
22 | github:
23 | client-id: ${GITHUB_CLIENT:}
24 | client-secret: ${GITHUB_SECRET:}
25 | scope: user:email
26 | mvc:
27 | hiddenmethod:
28 | filter:
29 | enabled: true
30 |
31 | mobilele:
32 | default.admin.pass: ${DEFAULT_ADMIN_PASS:topsecret}
33 | remember.me.key: ${REMEBER_ME_KEY:topsecret}
34 |
35 |
36 | logging:
37 | level:
38 | org.hibernate.SQL: DEBUG
39 | org.hibernate.orm.jdbc.bind: TRACE
40 |
41 |
42 | open.exchange.rates:
43 | schema: https
44 | host: openexchangerates.org
45 | path: /api/latest.json
46 | app_id: ${APP_ID:test}
47 | symbols: EUR,BGN
48 | enabled: false
49 |
50 | google.recaptcha:
51 | site: ${RECAPTCHA_SITE_KEY:test}
52 | secret: ${RECAPTCHA_SITE_SECRET:test}
53 |
54 |
55 | mail:
56 | mobilele: "mobilele@example.com"
57 | host: localhost
58 | port: 1025
59 | username: ${MAIL_USER:softuni}
60 | password: ${MAIL_PASSWORD:softuni}
61 |
62 | management:
63 | server:
64 | port: 8081
65 | endpoints:
66 | web:
67 | base-path: /actuator
68 | exposure:
69 | include: "*"
70 |
--------------------------------------------------------------------------------
/mobilele/src/main/resources/data.sql:
--------------------------------------------------------------------------------
1 | INSERT INTO users (id, active, email, first_name, last_name, password)
2 | VALUES
3 | (1, 1, 'admin@example.com', 'Admin', 'Adminov', '95c1933b8ffe84f085f2839899d1673260be58dbd9c2c787ac35515805502c996417596dae9a92880aaa50a046fc7151'),
4 | (2, 1, 'user@example.com', 'User', 'Userov', '95c1933b8ffe84f085f2839899d1673260be58dbd9c2c787ac35515805502c996417596dae9a92880aaa50a046fc7151'),
5 | (3, 1, 'lachezar.balev@gmail.com', 'Lachezar', 'Balev', '95c1933b8ffe84f085f2839899d1673260be58dbd9c2c787ac35515805502c996417596dae9a92880aaa50a046fc7151');
6 |
7 |
8 | INSERT INTO roles (`id`, `role`)
9 | VALUES
10 | (1, 'ADMIN'),
11 | (2, 'USER');
12 |
13 | INSERT INTO users_roles(`user_id`, `role_id`)
14 | VALUES
15 | (1, 1),
16 | (1, 2),
17 | (2, 2);
18 |
19 |
20 | INSERT INTO `brands` (`id`, `name`)
21 | VALUES
22 | (1, 'Toyota'),
23 | (2, 'Ford'),
24 | (3, 'Trabant');
25 |
26 | INSERT INTO `models` (`id`, `category`, `brand_id`, `name`)
27 | VALUES
28 | (1, 'CAR', 1, 'Camry'),
29 | (2, 'CAR', 1, 'Corolla'),
30 | (3, 'CAR', 2, 'Focus'),
31 | (4, 'CAR', 2, 'Fiesta'),
32 | (5, 'CAR', 3, '601');
33 |
34 | INSERT INTO `offers` (`id`, `description`, `engine`, `image_url`, `mileage`, `price`, `transmission`, `uuid`, `year`, `model_id`, `seller_id`)
35 | VALUES
36 | (1, 'Top Trabi 1!', 'PETROL', 'https://upload.wikimedia.org/wikipedia/commons/thumb/b/b8/AWZ_Trabant_601S%2C_Verkehrszentrum_des_Deutschen_Museums.JPG/1200px-AWZ_Trabant_601S%2C_Verkehrszentrum_des_Deutschen_Museums.JPG', 24000, 2223, 'MANUAL', 'b72e6550-e365-43bf-aab2-b57cafc2db7c', 1985, 5, 1),
37 | (2, 'Top Trabi 1!', 'PETROL', 'https://upload.wikimedia.org/wikipedia/commons/thumb/b/b8/AWZ_Trabant_601S%2C_Verkehrszentrum_des_Deutschen_Museums.JPG/1200px-AWZ_Trabant_601S%2C_Verkehrszentrum_des_Deutschen_Museums.JPG', 24000, 2225, 'MANUAL', 'b72e6550-e365-43bf-aab2-b57cafc2db71', 1986, 5, 1),
38 | (3, 'Top Trabi 2!', 'PETROL', 'https://upload.wikimedia.org/wikipedia/commons/thumb/b/b8/AWZ_Trabant_601S%2C_Verkehrszentrum_des_Deutschen_Museums.JPG/1200px-AWZ_Trabant_601S%2C_Verkehrszentrum_des_Deutschen_Museums.JPG', 24000, 2227, 'MANUAL', 'b72e6550-e365-43bf-aab2-b57cafc2db72', 1987, 5, 2),
39 | (4, 'Top Trabi 2!', 'PETROL', 'https://upload.wikimedia.org/wikipedia/commons/thumb/b/b8/AWZ_Trabant_601S%2C_Verkehrszentrum_des_Deutschen_Museums.JPG/1200px-AWZ_Trabant_601S%2C_Verkehrszentrum_des_Deutschen_Museums.JPG', 24000, 2221, 'MANUAL', 'b72e6550-e365-43bf-aab2-b57cafc2db73', 1988, 5, 2),
40 | (5, 'Top Trabi 2!', 'PETROL', 'https://upload.wikimedia.org/wikipedia/commons/thumb/b/b8/AWZ_Trabant_601S%2C_Verkehrszentrum_des_Deutschen_Museums.JPG/1200px-AWZ_Trabant_601S%2C_Verkehrszentrum_des_Deutschen_Museums.JPG', 24000, 2220, 'MANUAL', 'b72e6550-e365-43bf-aab2-b57cafc2db74', 1989, 5, 2);
41 |
--------------------------------------------------------------------------------
/mobilele/src/main/resources/static/css/main.css:
--------------------------------------------------------------------------------
1 | body {
2 | background-image: url('/images/1.jpg');
3 | background-position: center center;
4 | background-repeat: no-repeat;
5 | background-attachment: fixed;
6 | background-size: cover;
7 | background-color: #464646;
8 | }
9 |
10 | .navbar .logo {
11 | max-height: 50px;
12 | padding-right: 100px;
13 | }
14 |
15 | .container, .container-fluid {
16 | padding-top: 90px;
17 | }
18 |
19 | .bg-text {
20 | background-color: rgb(0,0,0); /* Fallback color */
21 | background-color: rgba(0,0,0, 0.4); /* Black w/opacity/see-through */
22 | color: white;
23 | font-weight: bold;
24 | border: 3px solid #f1f1f1;
25 | z-index: 2;
26 | width: 80%;
27 | padding: 20px;
28 | text-align: center;
29 | }
30 |
31 | .brand-section {
32 | background-image: linear-gradient(to bottom right, rgba(220, 220, 220, 0.9), rgba(255, 255, 255, 0.9));
33 | }
34 |
35 | td, th {
36 | padding: 3px 8px;
37 | }
38 |
39 | .card.offer, .offer-details .list-group-item {
40 | background-image: linear-gradient(to bottom right, rgba(220, 220, 220, 0.9), rgba(255, 255, 255, 0.9));
41 | }
42 | .card-img-top-wrapper {
43 | height: 150px;
44 | overflow:hidden;
45 | }
46 |
47 | form.main-form {
48 | background-image: linear-gradient(to bottom right, rgba(70, 70, 70, 0.8), rgba(10, 10, 10, 0.8));
49 | padding:10px 30px;
50 | border-radius: 20px;
51 | }
52 |
53 | .field-error {
54 | background: white url('data:image/svg+xml;utf8, ') no-repeat top right/30px 30px;
55 | }
56 |
57 | .logged-user {
58 | color: white;
59 | }
60 |
--------------------------------------------------------------------------------
/mobilele/src/main/resources/static/css/reset-css.css:
--------------------------------------------------------------------------------
1 | /* http://meyerweb.com/eric/tools/css/reset/
2 | v2.0 | 20110126
3 | License: none (public domain)
4 | */
5 |
6 | html, body, div, span, applet, object, iframe,
7 | h1, h2, h3, h4, h5, h6, p, blockquote, pre,
8 | a, abbr, acronym, address, big, cite, code,
9 | del, dfn, em, img, ins, kbd, q, s, samp,
10 | small, strike, strong, sub, sup, tt, var,
11 | b, u, i, center,
12 | dl, dt, dd, ol, ul, li,
13 | fieldset, form, label, legend,
14 | table, caption, tbody, tfoot, thead, tr, th, td,
15 | article, aside, canvas, details, embed,
16 | figure, figcaption, footer, header, hgroup,
17 | menu, nav, output, ruby, section, summary,
18 | time, mark, audio, video {
19 | margin: 0;
20 | padding: 0;
21 | border: 0;
22 | font-size: 100%;
23 | font: inherit;
24 | vertical-align: baseline;
25 | }
26 | /* HTML5 display-role reset for older browsers */
27 | article, aside, details, figcaption, figure,
28 | footer, header, hgroup, menu, nav, section {
29 | display: block;
30 | }
31 | body {
32 | line-height: 1;
33 | }
34 | ol, ul {
35 | list-style: none;
36 | }
37 | blockquote, q {
38 | quotes: none;
39 | }
40 | blockquote:before, blockquote:after,
41 | q:before, q:after {
42 | content: '';
43 | content: none;
44 | }
45 | table {
46 | border-collapse: collapse;
47 | border-spacing: 0;
48 | }
--------------------------------------------------------------------------------
/mobilele/src/main/resources/static/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luchob/softuni-sep-2023/c640aaad1a3e34eba81af8ba5ed433ea3899f529/mobilele/src/main/resources/static/favicon.ico
--------------------------------------------------------------------------------
/mobilele/src/main/resources/static/images/1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luchob/softuni-sep-2023/c640aaad1a3e34eba81af8ba5ed433ea3899f529/mobilele/src/main/resources/static/images/1.jpg
--------------------------------------------------------------------------------
/mobilele/src/main/resources/static/images/broken-car.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luchob/softuni-sep-2023/c640aaad1a3e34eba81af8ba5ed433ea3899f529/mobilele/src/main/resources/static/images/broken-car.png
--------------------------------------------------------------------------------
/mobilele/src/main/resources/static/images/car-dealership-car-car-showroom.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luchob/softuni-sep-2023/c640aaad1a3e34eba81af8ba5ed433ea3899f529/mobilele/src/main/resources/static/images/car-dealership-car-car-showroom.jpg
--------------------------------------------------------------------------------
/mobilele/src/main/resources/static/images/car.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luchob/softuni-sep-2023/c640aaad1a3e34eba81af8ba5ed433ea3899f529/mobilele/src/main/resources/static/images/car.png
--------------------------------------------------------------------------------
/mobilele/src/main/resources/templates/auth-login.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
56 |
57 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
--------------------------------------------------------------------------------
/mobilele/src/main/resources/templates/details.html:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
Details
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | -
24 |
25 |
26 | • Mileage
27 | 12345
28 |
29 |
30 | • Price
31 | 123
32 |
33 |
34 | • Engine type
35 | PETROL
36 |
37 |
38 | • Transmission type
39 | TRANSMISSION
40 |
41 |
42 | • Seller
43 | Pesho Petrov
44 |
45 |
46 |
47 |
48 |
52 |
53 |
54 |
55 |
![Car image]()
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/mobilele/src/main/resources/templates/email/registration-email.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
7 |
8 | Hello
9 |
10 |
11 | Hello, Pesho!
12 | Welcome to Mobilele!
13 |
14 | Please follow the link in order to activate your account.
15 |
16 |
--------------------------------------------------------------------------------
/mobilele/src/main/resources/templates/error/404.html:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
Upps, the thingy that you are looking for is no longer here!
16 |
We are veeeery sorry.
17 |
![Broken car]()
18 |
19 |
Go home...
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/mobilele/src/main/resources/templates/error/500.html:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
Upps, something went terribly wrong!
16 |
We are veeeery sorry and we are working to resolve the problem.
17 |
![Broken car]()
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/mobilele/src/main/resources/templates/fragments/head.html:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 | MobiLeLeLe
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/mobilele/src/main/resources/templates/fragments/navbar.html:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/mobilele/src/main/resources/templates/index.html:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
Welcome to MobiLeLeLe,
16 |
17 |
18 |
19 |
20 | Anonymous!
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/mobilele/src/test/java/org/softuni/mobilele/MobileleApplicationTests.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele;
2 |
3 | import org.junit.jupiter.api.Test;
4 | import org.springframework.boot.test.context.SpringBootTest;
5 |
6 | @SpringBootTest
7 | class MobileleApplicationTests {
8 |
9 | @Test
10 | void contextLoads() {
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/mobilele/src/test/java/org/softuni/mobilele/testutils/DBInit.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele.testutils;
2 |
3 | import java.util.List;
4 | import org.softuni.mobilele.model.entity.UserRoleEntity;
5 | import org.softuni.mobilele.model.enums.UserRoleEnum;
6 | import org.softuni.mobilele.repository.UserRoleRepository;
7 | import org.springframework.beans.factory.annotation.Autowired;
8 | import org.springframework.boot.CommandLineRunner;
9 | import org.springframework.stereotype.Component;
10 |
11 | @Component
12 | public class DBInit implements CommandLineRunner {
13 |
14 | @Autowired
15 | private UserRoleRepository userRoleRepository;
16 |
17 | @Override
18 | public void run(String... args) throws Exception {
19 | if (userRoleRepository.count() == 0) {
20 | userRoleRepository.saveAll(List.of(
21 | new UserRoleEntity().setRole(UserRoleEnum.USER),
22 | new UserRoleEntity().setRole(UserRoleEnum.ADMIN)
23 | ));
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/mobilele/src/test/java/org/softuni/mobilele/testutils/TestDataUtil.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele.testutils;
2 |
3 | import java.math.BigDecimal;
4 | import java.util.List;
5 | import java.util.UUID;
6 | import org.softuni.mobilele.model.entity.BrandEntity;
7 | import org.softuni.mobilele.model.entity.ExchangeRateEntity;
8 | import org.softuni.mobilele.model.entity.ModelEntity;
9 | import org.softuni.mobilele.model.entity.OfferEntity;
10 | import org.softuni.mobilele.model.entity.UserEntity;
11 | import org.softuni.mobilele.model.entity.UserRoleEntity;
12 | import org.softuni.mobilele.model.enums.EngineEnum;
13 | import org.softuni.mobilele.model.enums.TransmissionEnum;
14 | import org.softuni.mobilele.model.enums.UserRoleEnum;
15 | import org.softuni.mobilele.repository.BrandRepository;
16 | import org.softuni.mobilele.repository.ExchangeRateRepository;
17 | import org.softuni.mobilele.repository.OfferRepository;
18 | import org.softuni.mobilele.repository.UserRepository;
19 | import org.softuni.mobilele.repository.UserRoleRepository;
20 | import org.springframework.beans.factory.annotation.Autowired;
21 | import org.springframework.stereotype.Component;
22 |
23 | @Component
24 | public class TestDataUtil {
25 |
26 | @Autowired
27 | private ExchangeRateRepository exchangeRateRepository;
28 |
29 | @Autowired
30 | private OfferRepository offerRepository;
31 |
32 | @Autowired
33 | private BrandRepository brandRepository;
34 |
35 | public void createExchangeRate(String currency, BigDecimal rate) {
36 | exchangeRateRepository.save(
37 | new ExchangeRateEntity().setCurrency(currency).setRate(rate)
38 | );
39 | }
40 |
41 |
42 | public OfferEntity createTestOffer(UserEntity owner) {
43 |
44 | // create test brand
45 | BrandEntity brandEntity = brandRepository.save(new BrandEntity()
46 | .setName("Test Brand")
47 | .setModels(List.of(
48 | new ModelEntity().setName("Test Model"),
49 | new ModelEntity().setName("Test Model1")
50 | )));
51 |
52 | // create test offer
53 | OfferEntity offer = new OfferEntity()
54 | .setModel(brandEntity.getModels().get(0))
55 | .setImageUrl("https://www.google.com")
56 | .setPrice(BigDecimal.valueOf(1000))
57 | .setYear(2020)
58 | .setUuid(UUID.randomUUID())
59 | .setDescription("Test Description")
60 | .setEngine(EngineEnum.PETROL)
61 | .setMileage(10000)
62 | .setTransmission(TransmissionEnum.MANUAL)
63 | .setSeller(owner);
64 |
65 | return offerRepository.save(offer);
66 | }
67 | //
68 | public void cleanUp() {
69 | exchangeRateRepository.deleteAll();
70 | offerRepository.deleteAll();
71 | brandRepository.deleteAll();
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/mobilele/src/test/java/org/softuni/mobilele/testutils/UserTestDataUtil.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele.testutils;
2 |
3 | import java.util.List;
4 | import org.softuni.mobilele.model.entity.UserEntity;
5 | import org.softuni.mobilele.model.enums.UserRoleEnum;
6 | import org.softuni.mobilele.repository.UserRepository;
7 | import org.softuni.mobilele.repository.UserRoleRepository;
8 | import org.springframework.beans.factory.annotation.Autowired;
9 | import org.springframework.stereotype.Component;
10 |
11 | @Component
12 | public class UserTestDataUtil {
13 |
14 | @Autowired
15 | private UserRepository userRepository;
16 |
17 | @Autowired
18 | private UserRoleRepository userRoleRepository;
19 |
20 | public UserEntity createTestUser(String email) {
21 | return createUser(email, List.of(UserRoleEnum.USER));
22 | }
23 |
24 | public UserEntity createTestAdmin(String email) {
25 | return createUser(email, List.of(UserRoleEnum.ADMIN));
26 | }
27 |
28 | private UserEntity createUser(String email, List roles) {
29 |
30 | var roleEntities = userRoleRepository.findAllByRoleIn(roles);
31 |
32 | UserEntity newUser = new UserEntity()
33 | .setActive(true)
34 | .setEmail(email)
35 | .setFirstName("Test user first")
36 | .setLastName("Test user last")
37 | .setRoles(
38 | roleEntities
39 | );
40 |
41 | return userRepository.save(newUser);
42 | }
43 |
44 | public void cleanUp() {
45 | userRepository.deleteAll();
46 | }
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/mobilele/src/test/java/org/softuni/mobilele/web/CurrencyRestControllerTestIT.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele.web;
2 |
3 | import static org.hamcrest.Matchers.is;
4 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
5 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
6 |
7 | import java.math.BigDecimal;
8 | import org.junit.jupiter.api.Test;
9 | import org.softuni.mobilele.testutils.TestDataUtil;
10 | import org.springframework.beans.factory.annotation.Autowired;
11 | import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
12 | import org.springframework.boot.test.context.SpringBootTest;
13 | import org.springframework.test.web.servlet.MockMvc;
14 | import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
15 |
16 | @SpringBootTest
17 | @AutoConfigureMockMvc
18 | class CurrencyRestControllerTestIT {
19 |
20 | @Autowired
21 | private MockMvc mockMvc;
22 |
23 | @Autowired
24 | private TestDataUtil testDataUtil;
25 |
26 | @Test
27 | public void testConvert() throws Exception {
28 |
29 | testDataUtil.createExchangeRate("EUR", BigDecimal.valueOf(0.54));
30 |
31 | this.mockMvc.perform(MockMvcRequestBuilders.get("/api/currency/convert")
32 | .param("target", "EUR")
33 | .param("amount", "1000")).
34 | andExpect(status().isOk()).
35 | andExpect(jsonPath("$.currency", is("EUR"))).
36 | andExpect(jsonPath("$.amount", is(540.0)))
37 | ;
38 |
39 | }
40 | }
--------------------------------------------------------------------------------
/mobilele/src/test/java/org/softuni/mobilele/web/UserRegistrationControllerTestIT.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mobilele.web;
2 |
3 | import static org.junit.jupiter.api.Assertions.*;
4 | import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
5 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
6 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view;
7 |
8 | import com.icegreen.greenmail.util.GreenMail;
9 | import com.icegreen.greenmail.util.ServerSetup;
10 | import jakarta.mail.internet.MimeMessage;
11 | import org.junit.jupiter.api.AfterEach;
12 | import org.junit.jupiter.api.Assertions;
13 | import org.junit.jupiter.api.BeforeEach;
14 | import org.junit.jupiter.api.Test;
15 | import org.springframework.beans.factory.annotation.Autowired;
16 | import org.springframework.beans.factory.annotation.Value;
17 | import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
18 | import org.springframework.boot.test.context.SpringBootTest;
19 | import org.springframework.test.web.servlet.MockMvc;
20 | import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
21 |
22 | @SpringBootTest
23 | @AutoConfigureMockMvc
24 | class UserRegistrationControllerTestIT {
25 |
26 | @Autowired
27 | private MockMvc mockMvc;
28 |
29 | @Value("${mail.port}")
30 | private int port;
31 |
32 | @Value("${mail.host}")
33 | private String host;
34 |
35 | @Value("${mail.username}")
36 | private String username;
37 |
38 | @Value("${mail.password}")
39 | private String password;
40 |
41 | private GreenMail greenMail;
42 |
43 | @BeforeEach
44 | void setUp() {
45 | greenMail = new GreenMail(new ServerSetup(port, host,"smtp"));
46 | greenMail.start();
47 | greenMail.setUser(username, password);
48 | }
49 |
50 | @AfterEach
51 | void tearDown() {
52 | greenMail.stop();
53 | }
54 |
55 | @Test
56 | void testRegistration() throws Exception {
57 | mockMvc.perform(
58 | MockMvcRequestBuilders.post("/users/register")
59 | .param("email", "pesho@softuni.bg")
60 | .param("firstName", "Pesho")
61 | .param("lastName", "Petrov")
62 | .param("password", "topsecret")
63 | .param("confirmPassword", "topsecret")
64 | .with(csrf())
65 | ).andExpect(status().is3xxRedirection())
66 | .andExpect(view().name("redirect:/"));
67 |
68 | greenMail.waitForIncomingEmail(1);
69 | MimeMessage[] receivedMessages = greenMail.getReceivedMessages();
70 |
71 | Assertions.assertEquals(1, receivedMessages.length);
72 | MimeMessage registrationMessage = receivedMessages[0];
73 |
74 | Assertions.assertTrue(registrationMessage.getContent().toString().contains("Pesho"));
75 | Assertions.assertEquals(1, registrationMessage.getAllRecipients().length);
76 | Assertions.assertEquals("pesho@softuni.bg", registrationMessage.getAllRecipients()[0].toString());
77 | }
78 | }
--------------------------------------------------------------------------------
/mobilele/src/test/resources/application.yaml:
--------------------------------------------------------------------------------
1 | spring:
2 | jpa:
3 | hibernate:
4 | ddl-auto: create-drop
5 | defer-datasource-initialization: true
6 | sql:
7 | init:
8 | mode: never
9 | mvc:
10 | hiddenmethod:
11 | filter:
12 | enabled: true
13 |
14 | mobilele:
15 | default.admin.pass: topsecret
16 | remember.me.key: topsecret
17 |
18 |
19 | logging:
20 | level:
21 | org.hibernate.SQL: DEBUG
22 | org.hibernate.orm.jdbc.bind: TRACE
23 |
24 |
25 | open.exchange.rates:
26 | schema: https
27 | host: dummy
28 | path: /api/latest.json
29 | app_id: test
30 | symbols: EUR,BGN
31 | enabled: false
32 |
33 |
34 | mail:
35 | mobilele: "mobilele@example.com"
36 | host: localhost
37 | port: 3333
38 | username: test@example.com
39 | password: topsecret
40 |
--------------------------------------------------------------------------------
/mvcfn/.gitignore:
--------------------------------------------------------------------------------
1 | HELP.md
2 | .gradle
3 | build/
4 | !gradle/wrapper/gradle-wrapper.jar
5 | !**/src/main/**/build/
6 | !**/src/test/**/build/
7 |
8 | ### STS ###
9 | .apt_generated
10 | .classpath
11 | .factorypath
12 | .project
13 | .settings
14 | .springBeans
15 | .sts4-cache
16 | bin/
17 | !**/src/main/**/bin/
18 | !**/src/test/**/bin/
19 |
20 | ### IntelliJ IDEA ###
21 | .idea
22 | *.iws
23 | *.iml
24 | *.ipr
25 | out/
26 | !**/src/main/**/out/
27 | !**/src/test/**/out/
28 |
29 | ### NetBeans ###
30 | /nbproject/private/
31 | /nbbuild/
32 | /dist/
33 | /nbdist/
34 | /.nb-gradle/
35 |
36 | ### VS Code ###
37 | .vscode/
38 |
--------------------------------------------------------------------------------
/mvcfn/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'java'
3 | id 'org.springframework.boot' version '3.1.3'
4 | id 'io.spring.dependency-management' version '1.1.3'
5 | }
6 |
7 | group = 'org.softuni'
8 | version = '0.0.1-SNAPSHOT'
9 |
10 | java {
11 | sourceCompatibility = '17'
12 | }
13 |
14 | repositories {
15 | mavenCentral()
16 | }
17 |
18 | dependencies {
19 | implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
20 | implementation 'org.springframework.boot:spring-boot-starter-web'
21 | testImplementation 'org.springframework.boot:spring-boot-starter-test'
22 | }
23 |
24 | tasks.named('test') {
25 | useJUnitPlatform()
26 | }
27 |
--------------------------------------------------------------------------------
/mvcfn/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luchob/softuni-sep-2023/c640aaad1a3e34eba81af8ba5ed433ea3899f529/mvcfn/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/mvcfn/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-bin.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/mvcfn/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'mvcfn'
2 |
--------------------------------------------------------------------------------
/mvcfn/src/main/java/org/softuni/mvcfn/MvcfnApplication.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mvcfn;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | @SpringBootApplication
7 | public class MvcfnApplication {
8 |
9 | public static void main(String[] args) {
10 | SpringApplication.run(MvcfnApplication.class, args);
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/mvcfn/src/main/java/org/softuni/mvcfn/config/RoutesConfig.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mvcfn.config;
2 |
3 | import static org.springframework.web.servlet.function.RouterFunctions.route;
4 |
5 | import org.softuni.mvcfn.web.TestHandlers;
6 | import org.springframework.context.annotation.Bean;
7 | import org.springframework.context.annotation.Configuration;
8 | import org.springframework.web.servlet.function.RouterFunction;
9 | import org.springframework.web.servlet.function.ServerResponse;
10 |
11 | @Configuration
12 | public class RoutesConfig {
13 |
14 | @Bean
15 | public RouterFunction routingFunction(TestHandlers testHandlers) {
16 | return route()
17 | .GET("/test", testHandlers::test)
18 | .build();
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/mvcfn/src/main/java/org/softuni/mvcfn/web/TestHandlers.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mvcfn.web;
2 |
3 | import java.util.Map;
4 | import org.apache.catalina.Server;
5 | import org.springframework.stereotype.Component;
6 | import org.springframework.web.servlet.function.ServerRequest;
7 | import org.springframework.web.servlet.function.ServerResponse;
8 |
9 | @Component
10 | public class TestHandlers {
11 | public ServerResponse test(ServerRequest serverRequest) {
12 | return ServerResponse.ok().render("test",
13 | Map.of("message", "Hello, Pesho!"));
14 | }
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/mvcfn/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/mvcfn/src/main/resources/templates/test.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
7 |
8 | Document
9 |
10 |
11 |
12 | The message here
13 |
14 |
15 |
--------------------------------------------------------------------------------
/mvcfn/src/test/java/org/softuni/mvcfn/MvcfnApplicationTests.java:
--------------------------------------------------------------------------------
1 | package org.softuni.mvcfn;
2 |
3 | import org.junit.jupiter.api.Test;
4 | import org.springframework.boot.test.context.SpringBootTest;
5 |
6 | @SpringBootTest
7 | class MvcfnApplicationTests {
8 |
9 | @Test
10 | void contextLoads() {
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/plain-web-app/.gitignore:
--------------------------------------------------------------------------------
1 | .gradle
2 | build/
3 | !gradle/wrapper/gradle-wrapper.jar
4 | !**/src/main/**/build/
5 | !**/src/test/**/build/
6 |
7 | ### IntelliJ IDEA ###
8 | .idea/modules.xml
9 | .idea/jarRepositories.xml
10 | .idea/compiler.xml
11 | .idea/libraries/
12 | .idea
13 | *.iws
14 | *.iml
15 | *.ipr
16 | out/
17 | !**/src/main/**/out/
18 | !**/src/test/**/out/
19 |
20 | ### Eclipse ###
21 | .apt_generated
22 | .classpath
23 | .factorypath
24 | .project
25 | .settings
26 | .springBeans
27 | .sts4-cache
28 | bin/
29 | !**/src/main/**/bin/
30 | !**/src/test/**/bin/
31 |
32 | ### NetBeans ###
33 | /nbproject/private/
34 | /nbbuild/
35 | /dist/
36 | /nbdist/
37 | /.nb-gradle/
38 |
39 | ### VS Code ###
40 | .vscode/
41 |
42 | ### Mac OS ###
43 | .DS_Store
--------------------------------------------------------------------------------
/plain-web-app/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'java'
3 | id 'war'
4 | }
5 |
6 | group 'bg.softuni'
7 | version '1.0-SNAPSHOT'
8 |
9 | repositories {
10 | mavenCentral()
11 | }
12 |
13 | ext {
14 | junitVersion = '5.9.2'
15 | }
16 |
17 | sourceCompatibility = '11'
18 | targetCompatibility = '11'
19 |
20 | tasks.withType(JavaCompile) {
21 | options.encoding = 'UTF-8'
22 | }
23 |
24 | dependencies {
25 | compileOnly('jakarta.servlet:jakarta.servlet-api:5.0.0')
26 |
27 | testImplementation("org.junit.jupiter:junit-jupiter-api:${junitVersion}")
28 | testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${junitVersion}")
29 | }
30 |
31 | test {
32 | useJUnitPlatform()
33 | }
--------------------------------------------------------------------------------
/plain-web-app/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luchob/softuni-sep-2023/c640aaad1a3e34eba81af8ba5ed433ea3899f529/plain-web-app/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/plain-web-app/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 |
--------------------------------------------------------------------------------
/plain-web-app/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = "plain-web-app"
--------------------------------------------------------------------------------
/plain-web-app/src/main/java/bg/softuni/plainwebapp/HelloFilter.java:
--------------------------------------------------------------------------------
1 | package bg.softuni.plainwebapp;
2 |
3 | import jakarta.servlet.Filter;
4 | import jakarta.servlet.FilterChain;
5 | import jakarta.servlet.ServletException;
6 | import jakarta.servlet.ServletRequest;
7 | import jakarta.servlet.ServletResponse;
8 | import jakarta.servlet.annotation.WebFilter;
9 | import java.io.IOException;
10 |
11 | @WebFilter(
12 | urlPatterns = "/hello-servlet"
13 | )
14 | public class HelloFilter implements Filter {
15 | // ---> Filter1 --> Filter2 ... --> Servlet -> Browser
16 | @Override
17 | public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
18 | throws IOException, ServletException {
19 | request.setAttribute("username", "Pesho");
20 | chain.doFilter(request, response);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/plain-web-app/src/main/java/bg/softuni/plainwebapp/HelloServlet.java:
--------------------------------------------------------------------------------
1 | package bg.softuni.plainwebapp;
2 |
3 | import java.io.*;
4 | import jakarta.servlet.http.*;
5 | import jakarta.servlet.annotation.*;
6 |
7 | @WebServlet(name = "helloServlet", value = "/hello-servlet")
8 | public class HelloServlet extends HttpServlet {
9 |
10 | public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
11 | response.setContentType("text/html");
12 |
13 | // Hello
14 | PrintWriter out = response.getWriter();
15 | out.println("");
16 | out.println("" + request.getAttribute("username") + "
");
17 | out.println("");
18 | }
19 | }
--------------------------------------------------------------------------------
/plain-web-app/src/main/webapp/WEB-INF/web.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
--------------------------------------------------------------------------------
/plain-web-app/src/main/webapp/index.jsp:
--------------------------------------------------------------------------------
1 | <%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
2 |
3 |
4 |
5 | JSP - Hello World
6 |
7 |
8 | <%= "Hello World!" %>
9 |
10 |
11 | Hello Servlet
12 |
13 |
--------------------------------------------------------------------------------
/proxies/.gitignore:
--------------------------------------------------------------------------------
1 | .gradle
2 | build/
3 | !gradle/wrapper/gradle-wrapper.jar
4 | !**/src/main/**/build/
5 | !**/src/test/**/build/
6 |
7 | ### IntelliJ IDEA ###
8 | .idea
9 | .idea/modules.xml
10 | .idea/jarRepositories.xml
11 | .idea/compiler.xml
12 | .idea/libraries/
13 | *.iws
14 | *.iml
15 | *.ipr
16 | out/
17 | !**/src/main/**/out/
18 | !**/src/test/**/out/
19 |
20 | ### Eclipse ###
21 | .apt_generated
22 | .classpath
23 | .factorypath
24 | .project
25 | .settings
26 | .springBeans
27 | .sts4-cache
28 | bin/
29 | !**/src/main/**/bin/
30 | !**/src/test/**/bin/
31 |
32 | ### NetBeans ###
33 | /nbproject/private/
34 | /nbbuild/
35 | /dist/
36 | /nbdist/
37 | /.nb-gradle/
38 |
39 | ### VS Code ###
40 | .vscode/
41 |
42 | ### Mac OS ###
43 | .DS_Store
--------------------------------------------------------------------------------
/proxies/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'java'
3 | }
4 |
5 | group = 'org.example'
6 | version = '1.0-SNAPSHOT'
7 |
8 | repositories {
9 | mavenCentral()
10 | }
11 |
12 | dependencies {
13 | testImplementation platform('org.junit:junit-bom:5.9.1')
14 | testImplementation 'org.junit.jupiter:junit-jupiter'
15 | }
16 |
17 | test {
18 | useJUnitPlatform()
19 | }
--------------------------------------------------------------------------------
/proxies/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luchob/softuni-sep-2023/c640aaad1a3e34eba81af8ba5ed433ea3899f529/proxies/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/proxies/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Nov 06 21:04:36 EET 2023
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
5 | zipStoreBase=GRADLE_USER_HOME
6 | zipStorePath=wrapper/dists
7 |
--------------------------------------------------------------------------------
/proxies/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'proxies'
2 |
3 |
--------------------------------------------------------------------------------
/proxies/src/main/java/bg/softuni/proxies/Cacheable.java:
--------------------------------------------------------------------------------
1 | package bg.softuni.proxies;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | @Retention(RetentionPolicy.RUNTIME)
9 | @Target(ElementType.METHOD)
10 | public @interface Cacheable {
11 | String value();
12 | }
13 |
--------------------------------------------------------------------------------
/proxies/src/main/java/bg/softuni/proxies/CacheableInvocationHandler.java:
--------------------------------------------------------------------------------
1 | package bg.softuni.proxies;
2 |
3 | import java.lang.reflect.InvocationHandler;
4 | import java.lang.reflect.Method;
5 | import java.util.Map;
6 | import java.util.concurrent.ConcurrentHashMap;
7 |
8 | public class CacheableInvocationHandler implements InvocationHandler {
9 |
10 | private final Map cache = new ConcurrentHashMap<>();
11 | private final Object realObject;
12 |
13 | public CacheableInvocationHandler(Object realObject) {
14 | this.realObject = realObject;
15 | }
16 |
17 | @Override
18 | public Object invoke(
19 | Object proxy,
20 | Method method,
21 | Object[] args) throws Throwable {
22 |
23 | Cacheable cacheableAnnotation =
24 | realObject
25 | .getClass()
26 | .getMethod(method.getName(), method.getParameterTypes())
27 | .getAnnotation(Cacheable.class);
28 |
29 | if (cacheableAnnotation != null) {
30 | String cacheKey = cacheableAnnotation.value();
31 | if (cache.containsKey(cacheKey)) {
32 | return cache.get(cacheKey);
33 | } else {
34 | Object value = method.invoke(realObject, args);
35 | cache.put(cacheKey, value);
36 | return value;
37 | }
38 | } else {
39 | return method.invoke(realObject, args);
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/proxies/src/main/java/bg/softuni/proxies/Main.java:
--------------------------------------------------------------------------------
1 | package bg.softuni.proxies;
2 |
3 | import java.lang.reflect.Proxy;
4 |
5 | public class Main {
6 |
7 | public static void main(String[] args) {
8 |
9 | StudentServiceIfc studentServiceIfc = createStudentService();
10 | studentServiceIfc.getAllStudents();
11 | studentServiceIfc.getAllStudents();
12 | studentServiceIfc.getAllStudents();
13 | }
14 |
15 | public static StudentServiceIfc createStudentService() {
16 | return (StudentServiceIfc)Proxy.newProxyInstance(
17 | Main.class.getClassLoader(),
18 | new Class[]{StudentServiceIfc.class},
19 | new CacheableInvocationHandler(new StudentService())
20 | );
21 | }
22 | }
--------------------------------------------------------------------------------
/proxies/src/main/java/bg/softuni/proxies/StudentDTO.java:
--------------------------------------------------------------------------------
1 | package bg.softuni.proxies;
2 |
3 | public record StudentDTO(String name, int age) {
4 |
5 | }
6 |
--------------------------------------------------------------------------------
/proxies/src/main/java/bg/softuni/proxies/StudentService.java:
--------------------------------------------------------------------------------
1 | package bg.softuni.proxies;
2 |
3 | import java.util.List;
4 |
5 | public class StudentService implements StudentServiceIfc {
6 | @Override
7 | @Cacheable("students")
8 | public List getAllStudents() {
9 |
10 | System.out.println("CALCULATING ALL STUDENTS");
11 |
12 | return List.of(
13 | new StudentDTO("Pesho", 21),
14 | new StudentDTO("Anna", 21)
15 | );
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/proxies/src/main/java/bg/softuni/proxies/StudentServiceIfc.java:
--------------------------------------------------------------------------------
1 | package bg.softuni.proxies;
2 |
3 | import java.util.List;
4 |
5 | public interface StudentServiceIfc {
6 | List getAllStudents();
7 | }
8 |
--------------------------------------------------------------------------------
/thymeleaf-pure/.gitignore:
--------------------------------------------------------------------------------
1 | .gradle
2 | build/
3 | !gradle/wrapper/gradle-wrapper.jar
4 | !**/src/main/**/build/
5 | !**/src/test/**/build/
6 |
7 | ### IntelliJ IDEA ###
8 | .idea/
9 | .idea/modules.xml
10 | .idea/jarRepositories.xml
11 | .idea/compiler.xml
12 | .idea/libraries/
13 | *.iws
14 | *.iml
15 | *.ipr
16 | out/
17 | !**/src/main/**/out/
18 | !**/src/test/**/out/
19 |
20 | ### Eclipse ###
21 | .apt_generated
22 | .classpath
23 | .factorypath
24 | .project
25 | .settings
26 | .springBeans
27 | .sts4-cache
28 | bin/
29 | !**/src/main/**/bin/
30 | !**/src/test/**/bin/
31 |
32 | ### NetBeans ###
33 | /nbproject/private/
34 | /nbbuild/
35 | /dist/
36 | /nbdist/
37 | /.nb-gradle/
38 |
39 | ### VS Code ###
40 | .vscode/
41 |
42 | ### Mac OS ###
43 | .DS_Store
--------------------------------------------------------------------------------
/thymeleaf-pure/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'java'
3 | }
4 |
5 | group = 'org.example'
6 | version = '1.0-SNAPSHOT'
7 |
8 | repositories {
9 | mavenCentral()
10 | }
11 |
12 | dependencies {
13 |
14 | implementation "org.thymeleaf:thymeleaf:3.1.2.RELEASE"
15 |
16 | testImplementation platform('org.junit:junit-bom:5.9.1')
17 | testImplementation 'org.junit.jupiter:junit-jupiter'
18 | }
19 |
20 | test {
21 | useJUnitPlatform()
22 | }
--------------------------------------------------------------------------------
/thymeleaf-pure/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luchob/softuni-sep-2023/c640aaad1a3e34eba81af8ba5ed433ea3899f529/thymeleaf-pure/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/thymeleaf-pure/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Oct 02 19:00:53 EEST 2023
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
5 | zipStoreBase=GRADLE_USER_HOME
6 | zipStorePath=wrapper/dists
7 |
--------------------------------------------------------------------------------
/thymeleaf-pure/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'thymeleaf-pure'
2 |
3 |
--------------------------------------------------------------------------------
/thymeleaf-pure/src/main/java/org/example/Main.java:
--------------------------------------------------------------------------------
1 | package org.example;
2 |
3 | import org.thymeleaf.ITemplateEngine;
4 | import org.thymeleaf.TemplateEngine;
5 | import org.thymeleaf.context.Context;
6 | import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver;
7 |
8 | public class Main {
9 |
10 | public static void main(String[] args) {
11 | ITemplateEngine templateEngine = createThymeleafTemplateEngine();
12 |
13 | Context ctx = new Context();
14 | ctx.setVariable("name", "Pesho");
15 |
16 | String html = templateEngine.process("test.html", ctx);
17 | System.out.println(html);
18 | }
19 |
20 | private static ITemplateEngine createThymeleafTemplateEngine() {
21 |
22 | TemplateEngine engine = new TemplateEngine();
23 |
24 | engine.setTemplateResolver(new ClassLoaderTemplateResolver());
25 |
26 | return engine;
27 | }
28 | }
--------------------------------------------------------------------------------
/thymeleaf-pure/src/main/resources/test.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
7 |
8 | Document
9 |
10 |
11 | Hello,
12 |
13 |
14 |
--------------------------------------------------------------------------------