Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Комментарии в Pull Request обязательно должны быть на русском языке!
11 changes: 11 additions & 0 deletions .run/Deployment compose-local.run.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Deployment compose-local" type="docker-deploy" factoryName="docker-compose.yml" server-name="Docker">
<deployment type="docker-compose.yml">
<settings>
<option name="envFilePath" value="" />
<option name="sourceFilePath" value="compose-local.yaml" />
</settings>
</deployment>
<method v="2" />
</configuration>
</component>
11 changes: 11 additions & 0 deletions .run/MainApplication (local).run.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="MainApplication (local)" type="Application" factoryName="Application">
<option name="ALTERNATIVE_JRE_PATH" value="21"/>
<option name="MAIN_CLASS_NAME" value="ru.practicum.MainApplication"/>
<module name="main-service"/>
<option name="VM_PARAMETERS" value="-Dspring.profiles.active=local"/>
<method v="2">
<option name="Make" enabled="true"/>
</method>
</configuration>
</component>
10 changes: 10 additions & 0 deletions .run/MainApplication.run.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="MainApplication" type="Application" factoryName="Application">
<option name="MAIN_CLASS_NAME" value="ru.practicum.MainApplication" />
<module name="main-service" />
<option name="VM_PARAMETERS" value="-Dspring.profiles.active=test" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
</component>
11 changes: 11 additions & 0 deletions .run/StatApplication (local).run.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="StatApplication (local)" type="Application" factoryName="Application">
<option name="ALTERNATIVE_JRE_PATH" value="21"/>
<option name="MAIN_CLASS_NAME" value="ru.practicum.StatApplication"/>
<module name="stats-server"/>
<option name="VM_PARAMETERS" value="-Dspring.profiles.active=local"/>
<method v="2">
<option name="Make" enabled="true"/>
</method>
</configuration>
</component>
11 changes: 11 additions & 0 deletions .run/StatApplication.run.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="StatApplication" type="Application" factoryName="Application">
<option name="ALTERNATIVE_JRE_PATH" value="21"/>
<option name="MAIN_CLASS_NAME" value="ru.practicum.StatApplication"/>
<module name="stats-server"/>
<option name="VM_PARAMETERS" value="-Dspring.profiles.active=test"/>
<method v="2">
<option name="Make" enabled="true"/>
</method>
</configuration>
</component>
57 changes: 57 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Комментарии — API (техническое задание)

Краткое описание: набор REST-эндпоинтов для управления комментариями. Три уровня доступа:
- Admin — администраторские операции (удаление комментария по его идентификатору).
- Private — операции пользователя над собственными комментариями (создать, изменить, удалить, получить все свои комментарии).
- Public — публичный доступ (просмотр комментариев конкретного события).

---

## AdminCommentController
Цель: операции управления всеми комментариями.

- DELETE `/admin/comments/{commentId}`
- Описание: удалить комментарий (жёсткое удаление) по его идентификатору.
- Ограничения: без модерации комментариев.
- Права: только админ.
- Ответ: `204 No Content` при успехе.

---

## PrivateCommentController
Цель: пользовательские операции над собственными комментариями.

- POST `/users/{userId}/comments/{eventId}`
- Описание: создать новый комментарий от имени пользователя `userId`.
- Ограничения: длина комментария 5000 символов, проверка существования идентификаторов события и пользователя.
- Права: только аутентифицированный пользователь.
- Ответ: `201 Created` с созданным объектом.

- PATCH `/users/{userId}/comments/{eventId}/{commentId}`
- Описание: обновить собственный комментарий.
- Ограничения: длина комментария 5000 символов, проверка существования идентификаторов комментария, события и пользователя.
- Права: только автор комментария.
- Ответ: обновлённый объект комментария.

- DELETE `/users/{userId}/comments/{commentId}`
- Описание: удалить собственный комментарий (жёсткое удаление).
- Ограничения: проверка существования идентификаторов комментария и пользователя.
- Права: только автор комментария.
- Ответ: `204 No Content`.

- GET `/users/{userId}/comments`
- Описание: получить список собственных комментариев.
- Ограничения: организовать пагинацию (по умолчанию по 10 комментариев на странице).
- Права: только аутентифицированный пользователь.
- Ответ: список комментариев.

---

## PublicCommentController
Цель: публичный просмотр комментариев.

- GET `/comments/event/{eventId}`
- Описание: получить публичный список комментариев к событию.
- Ограничения: организовать пагинацию (по умолчанию по 10 комментариев на странице).
- Доступ: публичный (без авторизации).
- Ответ: список публичных комментариев.
5 changes: 5 additions & 0 deletions core/main-service/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
FROM eclipse-temurin:21-jre-jammy
VOLUME /tmp
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["sh", "-c", "java ${JAVA_OPTS} -jar /app.jar"]
106 changes: 106 additions & 0 deletions core/main-service/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>ru.yandex.practicum</groupId>
<artifactId>core</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>

<artifactId>main-service</artifactId>

<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>

<dependency>
<groupId>ru.yandex.practicum</groupId>
<artifactId>stats-client</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>

<dependency>
<groupId>ru.yandex.practicum</groupId>
<artifactId>stats-dto</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
</dependency>

<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${mapstruct.version}</version>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
</plugins>
</build>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package ru.yandex.practicum;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
import org.springframework.cloud.openfeign.EnableFeignClients;

@ConfigurationPropertiesScan
@EnableFeignClients
@SpringBootApplication
public class MainApplication {
public static void main(String[] args) {
SpringApplication.run(MainApplication.class, args);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package ru.yandex.practicum.category.controller;

import jakarta.validation.Valid;
import jakarta.validation.constraints.Positive;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import ru.yandex.practicum.category.dto.CategoryDto;
import ru.yandex.practicum.category.dto.NewCategoryDto;
import ru.yandex.practicum.category.service.CategoryService;

@Slf4j
@Validated
@RestController
@RequestMapping("/admin/categories")
@RequiredArgsConstructor
public class AdminCategoryController {
private final CategoryService categoryService;

@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public CategoryDto createCategory(@RequestBody @Valid NewCategoryDto newCategoryDto) {
log.debug("Controller: createCategory data={}", newCategoryDto);
return categoryService.createCategory(newCategoryDto);
}

@DeleteMapping("/{categoryId}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void deleteCategory(@PathVariable @Positive Long categoryId) {
log.debug("Controller: deleteCategory categoryId={}", categoryId);
categoryService.deleteCategory(categoryId);
}

@PatchMapping("/{categoryId}")
public CategoryDto updateCategory(@PathVariable @Positive Long categoryId,
@RequestBody @Valid NewCategoryDto newCategoryDto) {
log.debug("Controller: updateCategory categoryId={}, data={}", categoryId, newCategoryDto);
return categoryService.updateCategory(categoryId, newCategoryDto);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package ru.yandex.practicum.category.controller;

import jakarta.validation.constraints.Positive;
import jakarta.validation.constraints.PositiveOrZero;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import ru.yandex.practicum.category.dto.CategoryDto;
import ru.yandex.practicum.category.service.CategoryService;

import java.util.List;

@Slf4j
@Validated
@RestController
@RequestMapping("/categories")
@RequiredArgsConstructor
public class PublicCategoryController {
private final CategoryService categoryService;

@GetMapping
public List<CategoryDto> getCategories(
@RequestParam(defaultValue = "0") @PositiveOrZero Integer from,
@RequestParam(defaultValue = "10") @Positive Integer size) {
Pageable pageable = PageRequest.of(from / size, size);
log.debug("Controller: getCategories from={}, size={}", from, size);
return categoryService.getCategories(pageable);
}

@GetMapping("/{categoryId}")
public CategoryDto getCategoryById(@PathVariable @Positive Long categoryId) {
log.debug("Controller: getCategoryById categoryId={}", categoryId);
return categoryService.getCategoryById(categoryId);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package ru.yandex.practicum.category.dto;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;

public record CategoryDto(
Long id,

@NotBlank(message = "Название категории не может быть пустым")
@Size(min = 1, max = 50, message = "Название категории должно содержать от {min} до {max} символов")
String name
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package ru.yandex.practicum.category.dto;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;

public record NewCategoryDto(
@NotBlank(message = "Название категории не может быть пустым")
@Size(min = 1, max = 50, message = "Название категории должно содержать от {min} до {max} символов")
String name
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package ru.yandex.practicum.category.mapper;

import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingTarget;
import ru.yandex.practicum.category.dto.CategoryDto;
import ru.yandex.practicum.category.dto.NewCategoryDto;
import ru.yandex.practicum.category.model.Category;

@Mapper(componentModel = "spring")
public interface CategoryMapper {

@Mapping(target = "id", ignore = true)
Category toEntity(NewCategoryDto newCategoryDto);

CategoryDto toDto(Category category);

@Mapping(target = "id", ignore = true)
void updateCategoryFromDto(NewCategoryDto dto, @MappingTarget Category category);

Category toEntityFromDto(CategoryDto dto);
}
Loading