diff --git a/podkorytova_yulia_lab_3/README.md b/podkorytova_yulia_lab_3/README.md new file mode 100644 index 0000000..ea2d7c0 --- /dev/null +++ b/podkorytova_yulia_lab_3/README.md @@ -0,0 +1,161 @@ +# Лабораторная работа 3. REST API, Gateway и синхронный обмен между микросервисами +### Задание на лабораторную работу +1. Создать 2 микросервиса, реализующих CRUD на связанных сущностях. +2. Реализовать механизм синхронного обмена сообщениями между микросервисами. +3. Реализовать шлюз на основе прозрачного прокси-сервера nginx. + +*** +### Как запустить лабораторную работу +Для сборки и запуска программ необходимо перейти в директорию с файлом `docker-compose.yaml` и выполнить команду: +``` +docker-compose up -d +``` +*** +### Описание работы +Были созданы два микросервиса на *java*, каждый реализует CRUD-операции: список записей, подробности конкретной записи, создание, удаление и изменение записи. +Микросервисы реализованы при помощи фреймворка *spring*. + +**Описание сущностей:** +- *user* - пользователь. Поля: *id* (уникальный идентификатор пользователя), *fullName* (ФИО), *phoneNumber* (номер телефона), *role* (роль - ученик/репетитор). +- *message* - сообщение. Поля: *id* (уникальный идентификатор сообщения), *text* (текст), *status* (статус - отправлено/получено/прочитано), *date* (дата отправки), *userId* (id пользователя). + +Сущности связаны отношением один-ко-многим (пользователь-сообщения). + +**Реализация синхронного обмена:** + +Синхронный обмен между сообщениями реализуется при помощи RestTemplate. +При получении информации о сообщении через GET запрос к user-service получаются данные о пользователе-отправителе сообщения. + +Пример реализации при получении записи о сообщении в `MessageService`: +``` +private final MessageRepository messageRepository; +private final RestTemplate restTemplate; +private final String URL = "http://user-service:8085/user/"; + +@Autowired +public MessageService(MessageRepository messageRepository, RestTemplate restTemplate) { + this.messageRepository = messageRepository; + this.restTemplate = restTemplate; +} + +@Transactional(readOnly = true) +public MessageWithUserInfoDto findMessage(Long id) { + final Message message = messageRepository.findById(id).orElse(null); + if (message == null) { + throw new MessageNotFoundException(id); + } + UserInfoDto userInfo = restTemplate.getForObject(URL + message.getUserId(), UserInfoDto.class); + return new MessageWithUserInfoDto(message, userInfo); +} +``` + +**Dockerfile** + +Используется базовый образ *openjdk:17-jdk*, на основе которого будет создан контейнер. +Внутри контейнера создается директория `/usr/src/app/`, после устанавливается рабочая директория и файлы из каталога, где находится Dockerfile, копируются внутрь контейнера в директорию `/usr/src/app/`. +Выполняется сборка проекта с помощью инструмента gradlew. Задается порт и указывается точка входа для контейнера. Запуск java-приложения осуществляется посредством запуска jar-файла. + +Содержимое `Dockerfile` для сервиса `user-service`: +``` +FROM openjdk:17 +RUN mkdir -p /usr/src/app/ +WORKDIR /usr/src/app/ +COPY . /usr/src/app/ +RUN ./gradlew clean build +EXPOSE 8085 +ENTRYPOINT ["java","-jar","build/libs/user-service-1.0-SNAPSHOT.jar"] +``` +Аналогично был составлен `Dockerfile` для сервиса `message-service`. + +**Файл конфигурации nginx.conf** + +Файл `nginx.conf` содержит конфигурацию для сервера *nginx*. +Конфигурация позволяет перенаправлять запросы, начинающиеся с /user-service/ на сервер user-service на порту 8085, а запросы, начинающиеся с /message-service/, на сервер message-service на порту 8086. +``` +server { +listen 80; +listen [::]:80; +server_name localhost; + + location /user-service/ { + proxy_pass http://user-service:8085/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Prefix /user-service; + } + + location /message-service/ { + proxy_pass http://message-service:8086/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Prefix /message-service; + } +} +``` + +**Файл конфигурации docker-compose.yml** + +В файле `docker-compose.yml` указана версия синтаксиса *Docker Compose*, определен список сервисов, которые будут развернуты в приложении, и определена сеть с именем "network", которая будет использоваться для связи между контейнерами. +Файл определяет три сервиса: *user-service*, *message-service* и *nginx*. Для сервисов user-service и message-service используются образы приложений, созданные при помощи Dockerfile. +Сервис *nginx* использует образ *nginx*. +Параметр *depends_on* определяет зависимости сервисов (*nginx* зависит от сервисов *user-service* и *message-service* и разворачивается только после их запуска), в *ports* настраивается проброс портов. +``` +version: '3' + +services: + user-service: + build: + context: /user-service + dockerfile: Dockerfile + ports: + - 8085:8085 + networks: + - network + + message-service: + build: + context: /message-service + dockerfile: Dockerfile + ports: + - 8086:8086 + networks: + - network + + nginx: + image: nginx + ports: + - 8087:80 + networks: + - network + volumes: + - ./nginx-conf:/etc/nginx/conf.d + depends_on: + - user-service + - message-service + +networks: + network: + driver: bridge +``` + +*** +### Скриншоты +***Результаты сборки и запуска программ в консоли*** +![](images/console.jpg) + +***Образы в Dockerhub*** +![](images/dockerhub1.jpg) + +***Контейнеры в Dockerhub*** +![](images/dockerhub2.jpg) + +***Информация по пользователю*** +![](images/user.jpg) + +***Информация по сообщению*** +![](images/message.jpg) + +### Ссылка на видео: +https://drive.google.com/file/d/19pw4LWiuzK2tDHlSSbKbxNWPsvbUzWwd/view?usp=sharing \ No newline at end of file diff --git a/podkorytova_yulia_lab_3/docker-compose.yml b/podkorytova_yulia_lab_3/docker-compose.yml new file mode 100644 index 0000000..ad68f62 --- /dev/null +++ b/podkorytova_yulia_lab_3/docker-compose.yml @@ -0,0 +1,36 @@ +version: '3' + +services: + user-service: + build: + context: /user-service + dockerfile: Dockerfile + ports: + - 8085:8085 + networks: + - network + + message-service: + build: + context: /message-service + dockerfile: Dockerfile + ports: + - 8086:8086 + networks: + - network + + nginx: + image: nginx + ports: + - 8087:80 + networks: + - network + volumes: + - ./nginx-conf:/etc/nginx/conf.d + depends_on: + - user-service + - message-service + +networks: + network: + driver: bridge \ No newline at end of file diff --git a/podkorytova_yulia_lab_3/images/console.JPG b/podkorytova_yulia_lab_3/images/console.JPG new file mode 100644 index 0000000..b4e97fa Binary files /dev/null and b/podkorytova_yulia_lab_3/images/console.JPG differ diff --git a/podkorytova_yulia_lab_3/images/dockerhub1.JPG b/podkorytova_yulia_lab_3/images/dockerhub1.JPG new file mode 100644 index 0000000..3dc7584 Binary files /dev/null and b/podkorytova_yulia_lab_3/images/dockerhub1.JPG differ diff --git a/podkorytova_yulia_lab_3/images/dockerhub2.JPG b/podkorytova_yulia_lab_3/images/dockerhub2.JPG new file mode 100644 index 0000000..cf5d147 Binary files /dev/null and b/podkorytova_yulia_lab_3/images/dockerhub2.JPG differ diff --git a/podkorytova_yulia_lab_3/images/message.JPG b/podkorytova_yulia_lab_3/images/message.JPG new file mode 100644 index 0000000..a19a393 Binary files /dev/null and b/podkorytova_yulia_lab_3/images/message.JPG differ diff --git a/podkorytova_yulia_lab_3/images/user.JPG b/podkorytova_yulia_lab_3/images/user.JPG new file mode 100644 index 0000000..caf9a56 Binary files /dev/null and b/podkorytova_yulia_lab_3/images/user.JPG differ diff --git a/podkorytova_yulia_lab_3/message-service/.gitignore b/podkorytova_yulia_lab_3/message-service/.gitignore new file mode 100644 index 0000000..c2065bc --- /dev/null +++ b/podkorytova_yulia_lab_3/message-service/.gitignore @@ -0,0 +1,37 @@ +HELP.md +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ diff --git a/podkorytova_yulia_lab_3/message-service/Dockerfile b/podkorytova_yulia_lab_3/message-service/Dockerfile new file mode 100644 index 0000000..0f0d13a --- /dev/null +++ b/podkorytova_yulia_lab_3/message-service/Dockerfile @@ -0,0 +1,7 @@ +FROM openjdk:17 +RUN mkdir -p /usr/src/app/ +WORKDIR /usr/src/app/ +COPY . /usr/src/app/ +RUN ./gradlew clean build +EXPOSE 8086 +ENTRYPOINT ["java","-jar","build/libs/message-service-1.0-SNAPSHOT.jar"] \ No newline at end of file diff --git a/podkorytova_yulia_lab_3/message-service/build.gradle b/podkorytova_yulia_lab_3/message-service/build.gradle new file mode 100644 index 0000000..f3af670 --- /dev/null +++ b/podkorytova_yulia_lab_3/message-service/build.gradle @@ -0,0 +1,42 @@ +plugins { + id 'org.springframework.boot' version '2.6.3' + id 'io.spring.dependency-management' version '1.0.11.RELEASE' + id 'java' +} + +group 'com.example' +version '1.0-SNAPSHOT' +sourceCompatibility = '17' + +repositories { + mavenCentral() +} + +dependencies { + implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' + implementation 'org.springframework.boot:spring-boot-devtools' + implementation 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect' + + implementation 'org.webjars:bootstrap:5.1.3' + implementation 'org.webjars:jquery:3.6.0' + implementation 'org.webjars:font-awesome:6.1.0' + + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + implementation 'com.h2database:h2:2.1.210' + + implementation 'org.hibernate.validator:hibernate-validator' + + implementation 'org.springdoc:springdoc-openapi-ui:1.6.5' + + compileOnly 'org.projectlombok:lombok' + annotationProcessor 'org.projectlombok:lombok' + + testImplementation 'org.springframework.boot:spring-boot-starter-test' + + implementation group: 'org.springdoc', name: 'springdoc-openapi-ui', version: '1.6.5' +} + +tasks.named('test') { + useJUnitPlatform() +} \ No newline at end of file diff --git a/podkorytova_yulia_lab_3/message-service/gradlew b/podkorytova_yulia_lab_3/message-service/gradlew new file mode 100644 index 0000000..744e882 --- /dev/null +++ b/podkorytova_yulia_lab_3/message-service/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MSYS* | MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/podkorytova_yulia_lab_3/message-service/gradlew.bat b/podkorytova_yulia_lab_3/message-service/gradlew.bat new file mode 100644 index 0000000..107acd3 --- /dev/null +++ b/podkorytova_yulia_lab_3/message-service/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/podkorytova_yulia_lab_3/message-service/settings.gradle b/podkorytova_yulia_lab_3/message-service/settings.gradle new file mode 100644 index 0000000..c8f9f89 --- /dev/null +++ b/podkorytova_yulia_lab_3/message-service/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'message-service' \ No newline at end of file diff --git a/podkorytova_yulia_lab_3/message-service/src/main/java/com/example/app/MessageApp.java b/podkorytova_yulia_lab_3/message-service/src/main/java/com/example/app/MessageApp.java new file mode 100644 index 0000000..4e56f89 --- /dev/null +++ b/podkorytova_yulia_lab_3/message-service/src/main/java/com/example/app/MessageApp.java @@ -0,0 +1,18 @@ +package com.example.app; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.web.client.RestTemplate; + +@SpringBootApplication +public class MessageApp { + public static void main(String[] args) { + SpringApplication.run(MessageApp.class, args); + } + + @Bean + public RestTemplate restTemplate() { + return new RestTemplate(); + } +} diff --git a/podkorytova_yulia_lab_3/message-service/src/main/java/com/example/app/controller/MessageController.java b/podkorytova_yulia_lab_3/message-service/src/main/java/com/example/app/controller/MessageController.java new file mode 100644 index 0000000..a5bc784 --- /dev/null +++ b/podkorytova_yulia_lab_3/message-service/src/main/java/com/example/app/controller/MessageController.java @@ -0,0 +1,47 @@ +package com.example.app.controller; + +import com.example.app.dto.MessageDto; +import com.example.app.dto.MessageWithUserInfoDto; +import com.example.app.service.MessageService; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; +import java.util.*; +import java.util.stream.*; + +@RestController +@RequestMapping("/message") +public class MessageController { + private final MessageService messageService; + + public MessageController(MessageService messageService) { + this.messageService = messageService; + } + + @GetMapping("/") + public List getMessages() { + return messageService.findAllMessages().stream() + .map(MessageDto::new) + .collect(Collectors.toList()); + } + + @GetMapping("/{id}") + public MessageWithUserInfoDto getMessage(@PathVariable Long id) { + return messageService.findMessage(id); + } + + @PostMapping("/") + public MessageDto createMessage(@RequestBody @Valid MessageDto messageDto) { + return messageService.addMessage(messageDto); + } + + @PutMapping("/{id}") + public MessageDto updateMessage(@RequestBody @Valid MessageDto messageDto) { + return messageService.updateMessage(messageDto); + } + + @DeleteMapping("/{id}") + public MessageDto deleteMessage(@PathVariable Long id) { + return messageService.deleteMessage(id); + } +} \ No newline at end of file diff --git a/podkorytova_yulia_lab_3/message-service/src/main/java/com/example/app/dto/MessageDto.java b/podkorytova_yulia_lab_3/message-service/src/main/java/com/example/app/dto/MessageDto.java new file mode 100644 index 0000000..ddb67cf --- /dev/null +++ b/podkorytova_yulia_lab_3/message-service/src/main/java/com/example/app/dto/MessageDto.java @@ -0,0 +1,67 @@ +package com.example.app.dto; + +import com.example.app.model.Message; + +import java.sql.Date; + +import lombok.Data; + +@Data +public class MessageDto { + private Long id; + private String text; + private String status; + private Date date; + private Long userId; + + public MessageDto() { + } + + public MessageDto(Message message) { + this.id = message.getId(); + this.text = message.getText(); + this.status = message.getStatus(); + this.date = message.getDate(); + this.userId = message.getUserId(); + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public Date getDate() { + return date; + } + + public void setDate(Date date) { + this.date = date; + } + + public Long getUserId() { + return userId; + } + + public void setUserId(Long userId) { + this.userId = userId; + } +} diff --git a/podkorytova_yulia_lab_3/message-service/src/main/java/com/example/app/dto/MessageWithUserInfoDto.java b/podkorytova_yulia_lab_3/message-service/src/main/java/com/example/app/dto/MessageWithUserInfoDto.java new file mode 100644 index 0000000..bf7fad4 --- /dev/null +++ b/podkorytova_yulia_lab_3/message-service/src/main/java/com/example/app/dto/MessageWithUserInfoDto.java @@ -0,0 +1,68 @@ +package com.example.app.dto; + +import com.example.app.model.Message; +import lombok.Data; + +import java.sql.Date; + +@Data +public class MessageWithUserInfoDto { + private Long id; + private String text; + private String status; + private Date date; + private Long userId; + private UserInfoDto userInfo; + + public MessageWithUserInfoDto() { + } + + public MessageWithUserInfoDto(Message message, UserInfoDto userInfo) { + this.id = message.getId(); + this.text = message.getText(); + this.status = message.getStatus(); + this.date = message.getDate(); + this.userId = message.getUserId(); + this.userInfo = userInfo; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public Date getDate() { + return date; + } + + public void setDate(Date date) { + this.date = date; + } + + public Long getUserId() { + return userId; + } + + public void setUserId(Long userId) { + this.userId = userId; + } +} diff --git a/podkorytova_yulia_lab_3/message-service/src/main/java/com/example/app/dto/UserInfoDto.java b/podkorytova_yulia_lab_3/message-service/src/main/java/com/example/app/dto/UserInfoDto.java new file mode 100644 index 0000000..56382d4 --- /dev/null +++ b/podkorytova_yulia_lab_3/message-service/src/main/java/com/example/app/dto/UserInfoDto.java @@ -0,0 +1,43 @@ +package com.example.app.dto; + +import lombok.Data; + +@Data +public class UserInfoDto { + private Long id; + private String fullName; + private String phoneNumber; + private String role; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getFullName() { + return fullName; + } + + public void setFullName(String fullName) { + this.fullName = fullName; + } + + public String getPhoneNumber() { + return phoneNumber; + } + + public void setPhoneNumber(String phoneNumber) { + this.phoneNumber = phoneNumber; + } + + public String getRole() { + return role; + } + + public void setRole(String role) { + this.role = role; + } +} diff --git a/podkorytova_yulia_lab_3/message-service/src/main/java/com/example/app/model/Message.java b/podkorytova_yulia_lab_3/message-service/src/main/java/com/example/app/model/Message.java new file mode 100644 index 0000000..37d9295 --- /dev/null +++ b/podkorytova_yulia_lab_3/message-service/src/main/java/com/example/app/model/Message.java @@ -0,0 +1,77 @@ +package com.example.app.model; + +import javax.persistence.*; +import java.sql.Date; + +@Entity +@Table(name="messages") +public class Message { + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private Long id; + private String text; + private String status; + private Date date; + private Long userId; + + public Message() { + } + + public Message(String text, String status, Date date, Long userId) { + this.text = text; + this.status = status; + this.date = date; + this.userId = userId; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public Date getDate() { + return date; + } + + public void setDate(Date date) { + this.date = date; + } + + public Long getUserId() { + return userId; + } + + public void setUserId(Long userId) { + this.userId = userId; + } + + @Override + public String toString() { + return "Message{" + + "id=" + id + + ", text='" + text + '\'' + + ", status='" + status + '\'' + + ", date='" + date + '\'' + + ", userId='" + userId + '\'' + + '}'; + } +} diff --git a/podkorytova_yulia_lab_3/message-service/src/main/java/com/example/app/repository/MessageRepository.java b/podkorytova_yulia_lab_3/message-service/src/main/java/com/example/app/repository/MessageRepository.java new file mode 100644 index 0000000..a32c181 --- /dev/null +++ b/podkorytova_yulia_lab_3/message-service/src/main/java/com/example/app/repository/MessageRepository.java @@ -0,0 +1,7 @@ +package com.example.app.repository; + +import com.example.app.model.Message; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface MessageRepository extends JpaRepository { +} diff --git a/podkorytova_yulia_lab_3/message-service/src/main/java/com/example/app/service/MessageNotFoundException.java b/podkorytova_yulia_lab_3/message-service/src/main/java/com/example/app/service/MessageNotFoundException.java new file mode 100644 index 0000000..d81dbff --- /dev/null +++ b/podkorytova_yulia_lab_3/message-service/src/main/java/com/example/app/service/MessageNotFoundException.java @@ -0,0 +1,7 @@ +package com.example.app.service; + +public class MessageNotFoundException extends RuntimeException{ + public MessageNotFoundException(Long id) { + super(String.format("Message with id [%s] is not found", id)); + } +} \ No newline at end of file diff --git a/podkorytova_yulia_lab_3/message-service/src/main/java/com/example/app/service/MessageService.java b/podkorytova_yulia_lab_3/message-service/src/main/java/com/example/app/service/MessageService.java new file mode 100644 index 0000000..841250e --- /dev/null +++ b/podkorytova_yulia_lab_3/message-service/src/main/java/com/example/app/service/MessageService.java @@ -0,0 +1,70 @@ +package com.example.app.service; + +import com.example.app.dto.*; +import com.example.app.model.Message; +import com.example.app.repository.MessageRepository; +import org.springframework.beans.factory.annotation.*; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.client.RestTemplate; + +import java.util.*; + +@Service +public class MessageService { + private final MessageRepository messageRepository; + private final RestTemplate restTemplate; + private final String URL = "http://user-service:8085/user/"; + + @Autowired + public MessageService(MessageRepository messageRepository, RestTemplate restTemplate) { + this.messageRepository = messageRepository; + this.restTemplate = restTemplate; + } + + @Transactional(readOnly = true) + public MessageWithUserInfoDto findMessage(Long id) { + final Message message = messageRepository.findById(id).orElse(null); + if (message == null) { + throw new MessageNotFoundException(id); + } + UserInfoDto userInfo = restTemplate.getForObject(URL + message.getUserId(), UserInfoDto.class); + return new MessageWithUserInfoDto(message, userInfo); + } + + @Transactional + public MessageDto addMessage(MessageDto messageDto) { + final Message message = new Message(messageDto.getText(), messageDto.getStatus(), messageDto.getDate(), messageDto.getUserId()); + messageRepository.save(message); + return new MessageDto(message); + } + + @Transactional(readOnly = true) + public Message findMessageWithoutUserInfo(Long id) { + final Optional message = messageRepository.findById(id); + return message.orElseThrow(() -> new MessageNotFoundException(id)); + } + + @Transactional(readOnly = true) + public List findAllMessages() { + return messageRepository.findAll(); + } + + @Transactional + public MessageDto updateMessage(MessageDto messageDto) { + final Message message = findMessageWithoutUserInfo(messageDto.getId()); + message.setText(messageDto.getText()); + message.setStatus(messageDto.getStatus()); + message.setDate(messageDto.getDate()); + message.setUserId(messageDto.getUserId()); + messageRepository.save(message); + return new MessageDto(message); + } + + @Transactional + public MessageDto deleteMessage(Long id) { + final Message message = findMessageWithoutUserInfo(id); + messageRepository.delete(message); + return new MessageDto(message); + } +} diff --git a/podkorytova_yulia_lab_3/message-service/src/main/resources/application.properties b/podkorytova_yulia_lab_3/message-service/src/main/resources/application.properties new file mode 100644 index 0000000..db3df82 --- /dev/null +++ b/podkorytova_yulia_lab_3/message-service/src/main/resources/application.properties @@ -0,0 +1,11 @@ +spring.main.banner-mode=off +server.port=8086 +spring.datasource.url=jdbc:h2:file:./data +spring.datasource.driverClassName=org.h2.Driver +spring.datasource.username=sa +spring.datasource.password=password +spring.jpa.database-platform=org.hibernate.dialect.H2Dialect +spring.jpa.hibernate.ddl-auto=update +spring.h2.console.enabled=true +spring.h2.console.settings.trace=false +spring.h2.console.settings.web-allow-others=false diff --git a/podkorytova_yulia_lab_3/nginx-conf/nginx.conf b/podkorytova_yulia_lab_3/nginx-conf/nginx.conf new file mode 100644 index 0000000..4cd9e27 --- /dev/null +++ b/podkorytova_yulia_lab_3/nginx-conf/nginx.conf @@ -0,0 +1,21 @@ +server { + listen 80; + listen [::]:80; + server_name localhost; + + location /user-service/ { + proxy_pass http://user-service:8085/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Prefix /user-service; + } + + location /message-service/ { + proxy_pass http://message-service:8086/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Prefix /message-service; + } +} \ No newline at end of file diff --git a/podkorytova_yulia_lab_3/user-service/.gitignore b/podkorytova_yulia_lab_3/user-service/.gitignore new file mode 100644 index 0000000..c2065bc --- /dev/null +++ b/podkorytova_yulia_lab_3/user-service/.gitignore @@ -0,0 +1,37 @@ +HELP.md +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ diff --git a/podkorytova_yulia_lab_3/user-service/Dockerfile b/podkorytova_yulia_lab_3/user-service/Dockerfile new file mode 100644 index 0000000..67a0bcb --- /dev/null +++ b/podkorytova_yulia_lab_3/user-service/Dockerfile @@ -0,0 +1,7 @@ +FROM openjdk:17 +RUN mkdir -p /usr/src/app/ +WORKDIR /usr/src/app/ +COPY . /usr/src/app/ +RUN ./gradlew clean build +EXPOSE 8085 +ENTRYPOINT ["java","-jar","build/libs/user-service-1.0-SNAPSHOT.jar"] \ No newline at end of file diff --git a/podkorytova_yulia_lab_3/user-service/build.gradle b/podkorytova_yulia_lab_3/user-service/build.gradle new file mode 100644 index 0000000..878cd21 --- /dev/null +++ b/podkorytova_yulia_lab_3/user-service/build.gradle @@ -0,0 +1,40 @@ +plugins { + id 'org.springframework.boot' version '2.6.3' + id 'io.spring.dependency-management' version '1.0.11.RELEASE' + id 'java' +} + +group 'com.example' +version '1.0-SNAPSHOT' +sourceCompatibility = '17' + +repositories { + mavenCentral() +} + +dependencies { + implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' + implementation 'org.springframework.boot:spring-boot-devtools' + implementation 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect' + + implementation 'org.webjars:bootstrap:5.1.3' + implementation 'org.webjars:jquery:3.6.0' + implementation 'org.webjars:font-awesome:6.1.0' + + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + implementation 'com.h2database:h2:2.1.210' + + implementation 'org.hibernate.validator:hibernate-validator' + + implementation 'org.springdoc:springdoc-openapi-ui:1.6.5' + + compileOnly 'org.projectlombok:lombok' + annotationProcessor 'org.projectlombok:lombok' + + testImplementation 'org.springframework.boot:spring-boot-starter-test' +} + +tasks.named('test') { + useJUnitPlatform() +} \ No newline at end of file diff --git a/podkorytova_yulia_lab_3/user-service/gradlew b/podkorytova_yulia_lab_3/user-service/gradlew new file mode 100644 index 0000000..744e882 --- /dev/null +++ b/podkorytova_yulia_lab_3/user-service/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MSYS* | MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/podkorytova_yulia_lab_3/user-service/gradlew.bat b/podkorytova_yulia_lab_3/user-service/gradlew.bat new file mode 100644 index 0000000..107acd3 --- /dev/null +++ b/podkorytova_yulia_lab_3/user-service/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/podkorytova_yulia_lab_3/user-service/settings.gradle b/podkorytova_yulia_lab_3/user-service/settings.gradle new file mode 100644 index 0000000..4469b17 --- /dev/null +++ b/podkorytova_yulia_lab_3/user-service/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'user-service' \ No newline at end of file diff --git a/podkorytova_yulia_lab_3/user-service/src/main/java/com/example/app/UserApp.java b/podkorytova_yulia_lab_3/user-service/src/main/java/com/example/app/UserApp.java new file mode 100644 index 0000000..337674b --- /dev/null +++ b/podkorytova_yulia_lab_3/user-service/src/main/java/com/example/app/UserApp.java @@ -0,0 +1,11 @@ +package com.example.app; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class UserApp { + public static void main(String[] args) { + SpringApplication.run(UserApp.class, args); + } +} diff --git a/podkorytova_yulia_lab_3/user-service/src/main/java/com/example/app/controller/UserController.java b/podkorytova_yulia_lab_3/user-service/src/main/java/com/example/app/controller/UserController.java new file mode 100644 index 0000000..78ab0c2 --- /dev/null +++ b/podkorytova_yulia_lab_3/user-service/src/main/java/com/example/app/controller/UserController.java @@ -0,0 +1,46 @@ +package com.example.app.controller; + +import com.example.app.dto.UserDto; +import com.example.app.service.UserService; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; +import java.util.*; +import java.util.stream.Collectors; + +@RestController +@RequestMapping("/user") +public class UserController { + private final UserService userService; + + public UserController(UserService userService) { + this.userService = userService; + } + + @GetMapping("/") + public List getUsers() { + return userService.findAllUsers().stream() + .map(UserDto::new) + .collect(Collectors.toList()); + } + + @GetMapping("/{id}") + public UserDto getUser(@PathVariable Long id) { + return new UserDto(userService.findUser(id)); + } + + @PostMapping("/") + public UserDto createUser(@RequestBody @Valid UserDto userDto) { + return new UserDto(userService.addUser(userDto)); + } + + @PutMapping("/{id}") + public UserDto updateUser(@RequestBody @Valid UserDto userDto) { + return new UserDto(userService.updateUser(userDto)); + } + + @DeleteMapping("/{id}") + public UserDto deleteUser(@PathVariable Long id) { + return new UserDto(userService.deleteUser(id)); + } +} diff --git a/podkorytova_yulia_lab_3/user-service/src/main/java/com/example/app/dto/UserDto.java b/podkorytova_yulia_lab_3/user-service/src/main/java/com/example/app/dto/UserDto.java new file mode 100644 index 0000000..c42d19d --- /dev/null +++ b/podkorytova_yulia_lab_3/user-service/src/main/java/com/example/app/dto/UserDto.java @@ -0,0 +1,54 @@ +package com.example.app.dto; + +import com.example.app.model.User; +import lombok.Data; + +@Data +public class UserDto { + private Long id; + private String fullName; + private String phoneNumber; + private String role; + + public UserDto() { + } + + public UserDto(User user) { + this.id = user.getId(); + this.fullName = user.getFullName(); + this.phoneNumber = user.getPhoneNumber(); + this.role = user.getRole(); + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getFullName() { + return fullName; + } + + public void setFullName(String fullName) { + this.fullName = fullName; + } + + public String getPhoneNumber() { + return phoneNumber; + } + + public void setPhoneNumber(String phoneNumber) { + this.phoneNumber = phoneNumber; + } + + public String getRole() { + return role; + } + + public void setRole(String role) { + this.role = role; + } +} diff --git a/podkorytova_yulia_lab_3/user-service/src/main/java/com/example/app/model/User.java b/podkorytova_yulia_lab_3/user-service/src/main/java/com/example/app/model/User.java new file mode 100644 index 0000000..fbfc9c3 --- /dev/null +++ b/podkorytova_yulia_lab_3/user-service/src/main/java/com/example/app/model/User.java @@ -0,0 +1,65 @@ +package com.example.app.model; + +import javax.persistence.*; + +@Entity +@Table(name="users") +public class User { + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private Long id; + private String fullName; + private String phoneNumber; + private String role; + + public User() { + } + + public User(String fullName, String phoneNumber, String role) { + this.fullName = fullName; + this.phoneNumber = phoneNumber; + this.role = role; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getFullName() { + return fullName; + } + + public void setFullName(String fullName) { + this.fullName = fullName; + } + + public String getPhoneNumber() { + return phoneNumber; + } + + public void setPhoneNumber(String phoneNumber) { + this.phoneNumber = phoneNumber; + } + + public String getRole() { + return role; + } + + public void setRole(String role) { + this.role = role; + } + + @Override + public String toString() { + return "User{" + + "id=" + id + + ", fullName='" + fullName + '\'' + + ", phoneNumber='" + phoneNumber + '\'' + + ", role='" + role + '\'' + + '}'; + } +} diff --git a/podkorytova_yulia_lab_3/user-service/src/main/java/com/example/app/repository/UserRepository.java b/podkorytova_yulia_lab_3/user-service/src/main/java/com/example/app/repository/UserRepository.java new file mode 100644 index 0000000..c680c7e --- /dev/null +++ b/podkorytova_yulia_lab_3/user-service/src/main/java/com/example/app/repository/UserRepository.java @@ -0,0 +1,7 @@ +package com.example.app.repository; + +import com.example.app.model.User; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface UserRepository extends JpaRepository { +} diff --git a/podkorytova_yulia_lab_3/user-service/src/main/java/com/example/app/service/UserNotFoundException.java b/podkorytova_yulia_lab_3/user-service/src/main/java/com/example/app/service/UserNotFoundException.java new file mode 100644 index 0000000..67e97d2 --- /dev/null +++ b/podkorytova_yulia_lab_3/user-service/src/main/java/com/example/app/service/UserNotFoundException.java @@ -0,0 +1,7 @@ +package com.example.app.service; + +public class UserNotFoundException extends RuntimeException { + public UserNotFoundException(Long id) { + super(String.format("User with id [%s] is not found", id)); + } +} diff --git a/podkorytova_yulia_lab_3/user-service/src/main/java/com/example/app/service/UserService.java b/podkorytova_yulia_lab_3/user-service/src/main/java/com/example/app/service/UserService.java new file mode 100644 index 0000000..ba595eb --- /dev/null +++ b/podkorytova_yulia_lab_3/user-service/src/main/java/com/example/app/service/UserService.java @@ -0,0 +1,51 @@ +package com.example.app.service; + +import com.example.app.dto.UserDto; +import com.example.app.model.User; +import com.example.app.repository.UserRepository; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; + +@Service +public class UserService { + private final UserRepository userRepository; + + public UserService(UserRepository userRepository) { + this.userRepository = userRepository; + } + + @Transactional + public User addUser(UserDto userDto) { + final User user = new User(userDto.getFullName(), userDto.getPhoneNumber(), userDto.getRole()); + return userRepository.save(user); + } + + @Transactional(readOnly = true) + public User findUser(Long id) { + final Optional user = userRepository.findById(id); + return user.orElseThrow(() -> new UserNotFoundException(id)); + } + + @Transactional(readOnly = true) + public List findAllUsers() { + return userRepository.findAll(); + } + + @Transactional + public User updateUser(UserDto userDto) { + final User currentUser = findUser(userDto.getId()); + currentUser.setFullName(userDto.getFullName()); + currentUser.setPhoneNumber(userDto.getPhoneNumber()); + currentUser.setRole(userDto.getRole()); + return userRepository.save(currentUser); + } + + @Transactional + public User deleteUser(Long id) { + final User currentUser = findUser(id); + userRepository.delete(currentUser); + return currentUser; + } +} diff --git a/podkorytova_yulia_lab_3/user-service/src/main/resources/application.properties b/podkorytova_yulia_lab_3/user-service/src/main/resources/application.properties new file mode 100644 index 0000000..3ddc14e --- /dev/null +++ b/podkorytova_yulia_lab_3/user-service/src/main/resources/application.properties @@ -0,0 +1,11 @@ +spring.main.banner-mode=off +server.port=8085 +spring.datasource.url=jdbc:h2:file:./data +spring.datasource.driverClassName=org.h2.Driver +spring.datasource.username=sa +spring.datasource.password=password +spring.jpa.database-platform=org.hibernate.dialect.H2Dialect +spring.jpa.hibernate.ddl-auto=update +spring.h2.console.enabled=true +spring.h2.console.settings.trace=false +spring.h2.console.settings.web-allow-others=false