Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
10b087c
build(gradle): add jpa, postgres, update application.yml
jonasInDark Mar 4, 2026
d732a22
chore(resources.static): add new api specs, assets
jonasInDark Mar 4, 2026
f5450f2
feat(dto): update dto for new api specs
jonasInDark Mar 4, 2026
e6eab0b
feat(db, application.yml): init schema.sql, update application-dev.yml
jonasInDark Mar 4, 2026
fa547cc
feat(entity): init jpa entity
jonasInDark Mar 5, 2026
70cb409
feat(entity, repository): convert to jpa entity, jpa repository
jonasInDark Mar 5, 2026
b2a45f9
build(gradle): add mapstruct
jonasInDark Mar 6, 2026
35ff9b9
feat(dto): renewal UserServiceDto
jonasInDark Mar 6, 2026
54adc9a
feat(mapper): init mappers
jonasInDark Mar 6, 2026
aa42fb8
feat(service): update UserService
jonasInDark Mar 6, 2026
db7b597
feat(controller): update UserController
jonasInDark Mar 6, 2026
02bf617
feat(User): update entity, mapper, service
jonasInDark Mar 7, 2026
93c5cdf
feat(Channel): update dto, entity, mapper, service, controller
jonasInDark Mar 9, 2026
c183c0b
feat(Message): update entity, dto, mapper, service, controller
jonasInDark Mar 9, 2026
92be7eb
feat(BinaryContent): update entity, dto, mapper, service, controller
jonasInDark Mar 9, 2026
12d4e8f
feat(ReadStatus): update entity, dto, repository, service, controller…
jonasInDark Mar 9, 2026
040023a
feat(Auth): update dto, service, controller, UserRepository
jonasInDark Mar 10, 2026
af182bb
feat(UserStatus): remove useless features and init mapper and update …
jonasInDark Mar 10, 2026
dd2ab0c
feat(storage): init BinaryContentStorage and record class
jonasInDark Mar 10, 2026
73b787f
feat(User): update entity, dto, mapper, service, controller
jonasInDark Mar 11, 2026
7a3499d
build(gradle, docker-compose): add dependencies, change db port
jonasInDark Mar 15, 2026
2d32cbb
feat(Channel): try to resolve N+1 problem
jonasInDark Mar 15, 2026
2f683e3
feat(common): update advice, code, util
jonasInDark Mar 15, 2026
4a0657d
feat(entity, dto): update entity, dto
jonasInDark Mar 15, 2026
7d833bd
feat(mapper): update and init
jonasInDark Mar 15, 2026
6679970
refactor(service): rename BasicUserStatusService
jonasInDark Mar 15, 2026
ac5f713
feat(entity): add JoinTable
jonasInDark Mar 15, 2026
b42c961
refactor(dto, controller): change type, rename
jonasInDark Mar 15, 2026
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
44 changes: 37 additions & 7 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ plugins {
id 'java'
id 'org.springframework.boot' version '3.5.10'
id "io.spring.dependency-management" version '1.1.6'
id "com.autonomousapps.dependency-analysis" version "3.6.1"
}

group = 'com.sprint.mission'
Expand All @@ -16,21 +17,50 @@ dependencies {
testImplementation 'org.junit.jupiter:junit-jupiter'
implementation platform('org.springdoc:springdoc-openapi:2.8.5')

compileOnly("org.projectlombok:lombok:1.18.42")
annotationProcessor("org.projectlombok:lombok:1.18.42")
implementation('org.mapstruct:mapstruct:1.6.3')
annotationProcessor 'org.projectlombok:lombok-mapstruct-binding:0.2.0'
annotationProcessor('org.mapstruct:mapstruct-processor:1.6.3')

implementation('org.springframework.boot:spring-boot-starter-web')
implementation("org.springframework.boot:spring-boot-starter-actuator")
implementation('org.springframework.boot:spring-boot-starter-validation')
implementation('org.springdoc:springdoc-openapi-starter-webmvc-ui')
implementation('org.springframework.boot:spring-boot-starter-data-jpa')

compileOnly("org.projectlombok:lombok:1.18.42")
annotationProcessor("org.projectlombok:lombok:1.18.42")
implementation 'io.github.openfeign.querydsl:querydsl-jpa:7.1'
annotationProcessor 'io.github.openfeign.querydsl:querydsl-apt:7.1:jpa'
annotationProcessor 'jakarta.annotation:jakarta.annotation-api'
annotationProcessor 'jakarta.persistence:jakarta.persistence-api'

developmentOnly('org.springframework.boot:spring-boot-devtools')
runtimeOnly('org.postgresql:postgresql:42.7.7')

testCompileOnly("org.projectlombok:lombok:1.18.42")
testAnnotationProcessor("org.projectlombok:lombok:1.18.42")
testImplementation('org.springframework.boot:spring-boot-starter-test')
developmentOnly 'org.springframework.boot:spring-boot-devtools'
testImplementation 'net.datafaker:datafaker:2.5.4'
testImplementation('net.datafaker:datafaker:2.5.4')
}

def querydslDir = layout.buildDirectory.dir("generated/querydsl").get().asFile

sourceSets {
main.java.srcDirs += [ querydslDir ]
}

tasks.withType(JavaCompile).configureEach {
options.getGeneratedSourceOutputDirectory().set(querydslDir)
}

clean {
delete file(querydslDir)
}

jar {
enabled = false
}

test {
useJUnitPlatform()
}
//test {
// useJUnitPlatform()
//}
11 changes: 11 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
services:
db:
image: postgres
container_name: discodeit-A
restart: always
environment:
POSTGRES_USER: discodeit_user
POSTGRES_PASSWORD: discodeit1234
POSTGRES_DB: discodeit
ports:
- "5433:5432"
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#Mon Dec 02 14:48:55 KST 2024
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
53 changes: 0 additions & 53 deletions src/main/java/com/sprint/mission/discodeit/JavaApplication.java

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@
import com.sprint.mission.discodeit.common.exception.model.APIExceptionModel;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.http.ProblemDetail;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@RestControllerAdvice
public class APIExceptionAdvice {

@ExceptionHandler(APIException.class)
private ProblemDetail handleIdNotFound(APIException exception, HttpServletRequest request) {
private ProblemDetail handleAPIException(APIException exception, HttpServletRequest request) {
ErrorCode errorCode = exception.getErrorCode();
return APIExceptionModel.builder(errorCode.getStatus())
.detail(exception.getDetail())
Expand All @@ -21,4 +22,14 @@ private ProblemDetail handleIdNotFound(APIException exception, HttpServletReques
.uri(request.getRequestURI())
.build();
}

// todo
@ExceptionHandler(MethodArgumentNotValidException.class)
private ProblemDetail handleIncorrectInput(MethodArgumentNotValidException exception, HttpServletRequest request) {
return APIExceptionModel.builder(exception.getStatusCode().value())
.detail(exception.getLocalizedMessage())
.method(request.getMethod())
.uri(request.getRequestURI())
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@
public enum ErrorCode {
// common error
INTERNAL_SERVER_ERROR(500, "C001", "Internal server error"),
FILE_CANT_READ(500, "C002", "Failed to read file"),
ROOT_DIRECTORY_FAILED_TO_CREATE(500, "C003", "Failed to create root directories"),
FILE_CANT_WRITE(500, "C004", "Failed to save file"),
FILE_ALREADY_EXIST(500, "C005", "File already exists"),
FILE_NOT_FOUND(500, "C006", "File not found"),

// error related to user
USERID_NOT_FOUND(404, "U001", "User id not found"),
Expand All @@ -18,7 +23,7 @@ public enum ErrorCode {
// channel
CHANNELID_NOT_FOUND(404, "C001", "Channel id not found"),
NO_MESSAGE_IN_CHANNEL(404, "C002", "channel have no messages"),
PRIVATE_CHANNEL_NOT_UPDATE(400, "C003", "Private Channel can`t be updated"),
PRIVATE_CHANNEL_CANT_BE_UPDATED(400, "C003", "Private Channel can`t be updated"),

// message
MESSAGEID_NOT_FOUND(404, "M001", "Message id not found"),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
package com.sprint.mission.discodeit.common.util;

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.LocalDate;
import java.time.ZoneId;

public interface TimeConverter {
static LocalDateTime toDateTime(Instant time) {
return LocalDateTime.ofInstant(time, ZoneId.systemDefault());
static LocalDate toDateTime(Instant time) {
return LocalDate.ofInstant(time, ZoneId.systemDefault());
}

static Instant toInstant(LocalDateTime datetime) {
return datetime.atZone(ZoneId.systemDefault()).toInstant();
static Instant toInstant(LocalDate datetime) {
return datetime.atStartOfDay(ZoneId.systemDefault()).toInstant();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.sprint.mission.discodeit.config;

import com.querydsl.jpa.impl.JPAQueryFactory;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@RequiredArgsConstructor
public class QueryDslConfig {
@PersistenceContext
private final EntityManager entityManager;

@Bean
public JPAQueryFactory jpaQueryFactory() {
return new JPAQueryFactory(entityManager);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter;
Expand All @@ -13,6 +14,7 @@
import java.util.List;

@Configuration
@EnableJpaAuditing
@RequiredArgsConstructor
public class WebConfig implements WebMvcConfigurer {
private final ObjectMapper objectMapper;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
package com.sprint.mission.discodeit.controller;

import com.sprint.mission.discodeit.dto.AuthServiceDTO.LoginRequest;
import com.sprint.mission.discodeit.dto.user.UserServiceDTO;
import com.sprint.mission.discodeit.dto.auth.AuthServiceDTO.LoginRequest;
import com.sprint.mission.discodeit.dto.user.UserServiceDTO.UserResponse;
import com.sprint.mission.discodeit.service.AuthService;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/auth")
Expand All @@ -16,7 +19,7 @@ public class AuthController {
private final AuthService authService;

@PostMapping(value = "/login")
public ResponseEntity<UserResponse> login(@RequestBody LoginRequest request) {
public ResponseEntity<UserResponse> login(@RequestBody @Valid LoginRequest request) {
return ResponseEntity.status(HttpStatus.OK).body(authService.login(request));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.sprint.mission.discodeit.dto.binarycontent.BinaryContentServiceDTO.BinaryContentResponse;
import com.sprint.mission.discodeit.service.BinaryContentService;
import com.sprint.mission.discodeit.storage.BinaryContentStorage;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
Expand All @@ -15,6 +16,7 @@
@RequiredArgsConstructor
class BinaryContentController {
private final BinaryContentService binaryContentService;
private final BinaryContentStorage binaryContentStorage;

@GetMapping(value = "/{binaryContentId}")
public ResponseEntity<BinaryContentResponse> find(@PathVariable UUID binaryContentId) {
Expand All @@ -25,4 +27,10 @@ public ResponseEntity<BinaryContentResponse> find(@PathVariable UUID binaryConte
public ResponseEntity<List<BinaryContentResponse>> findMany(@RequestParam List<UUID> binaryContentIds) {
return ResponseEntity.status(HttpStatus.OK).body(binaryContentService.findAllByIdIn(binaryContentIds));
}

@GetMapping(value = "/{binaryContentId}/download")
public ResponseEntity<?> download(@PathVariable UUID binaryContentId) {
BinaryContentResponse dto = binaryContentService.find(binaryContentId);
return binaryContentStorage.download(dto);
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
package com.sprint.mission.discodeit.controller;

import com.sprint.mission.discodeit.dto.ChannelServiceDTO.*;
import com.sprint.mission.discodeit.dto.ReadStatusServiceDTO.ReadStatusCreateRequest;
import com.sprint.mission.discodeit.dto.ReadStatusServiceDTO.ReadStatusResponse;
import com.sprint.mission.discodeit.dto.ReadStatusServiceDTO.ReadStatusUpdateRequest;
import com.sprint.mission.discodeit.entity.ReadType;
import com.sprint.mission.discodeit.dto.channel.ChannelServiceDTO.ChannelDto;
import com.sprint.mission.discodeit.dto.channel.ChannelServiceDTO.ChannelResponse;
import com.sprint.mission.discodeit.dto.channel.request.PrivateChannelCreateRequest;
import com.sprint.mission.discodeit.dto.channel.request.PublicChannelCreateRequest;
import com.sprint.mission.discodeit.dto.channel.request.PublicChannelUpdateRequest;
import com.sprint.mission.discodeit.service.ChannelService;
import com.sprint.mission.discodeit.service.MessageService;
import com.sprint.mission.discodeit.service.ReadStatusService;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
Expand All @@ -22,40 +20,51 @@
@RequiredArgsConstructor
public class ChannelController {
private final ChannelService channelService;
private final MessageService messageService;
private final ReadStatusService readStatusService;

@PostMapping(value = "/public")
public ResponseEntity<ChannelResponse> createPublic(@RequestBody @Valid PublicChannelCreateRequest request) {
return ResponseEntity.status(HttpStatus.CREATED).body(channelService.createPublic(request));
public ResponseEntity<ChannelResponse> create(@RequestBody @Valid PublicChannelCreateRequest request) {
ChannelDto dto = ChannelDto.builder()
.name(request.name())
.description(request.description())
.type(request.type())
.build();
return ResponseEntity.status(HttpStatus.CREATED)
.body(channelService.createPublic(dto));
}

@PostMapping(value = "/private")
public ResponseEntity<ChannelResponse> createPrivate(@RequestBody @Valid PrivateChannelCreateRequest request) {
return ResponseEntity.status(HttpStatus.CREATED).body(channelService.createPrivate(request));
}

// deprecated ?
@RequestMapping(value = "/{id}", method = RequestMethod.GET)
public ResponseEntity<ChannelResponse> find(@PathVariable UUID id) {
return ResponseEntity.ok(channelService.find(id));
public ResponseEntity<ChannelResponse> create(@RequestBody @Valid PrivateChannelCreateRequest request) {
ChannelDto dto = ChannelDto.builder()
.participantIds(request.participantIds())
.type(request.type())
.build();
return ResponseEntity.status(HttpStatus.CREATED)
.body(channelService.createPrivate(dto));
}

@GetMapping
public ResponseEntity<List<ChannelResponse>> findByUserId(@RequestParam UUID userId) {
return ResponseEntity.status(HttpStatus.OK).body(channelService.findAllByUserId(userId));
return ResponseEntity.status(HttpStatus.OK)
.body(channelService.findAllByUserId(userId));
}

@PatchMapping(value = "/{id}")
public ResponseEntity<ChannelResponse> update(@PathVariable UUID id,
@RequestBody PublicChannelUpdateRequest request) {
PublicChannelUpdateCommand command = new PublicChannelUpdateCommand(id, request.newName(), request.newDescription());
return ResponseEntity.status(HttpStatus.OK).body(channelService.update(command));
public ResponseEntity<ChannelResponse> update(
@PathVariable UUID id,
@RequestBody PublicChannelUpdateRequest request) {
ChannelDto dto = ChannelDto.builder()
.id(id)
.name(request.name())
.description(request.description())
.type(request.type())
.build();
return ResponseEntity.status(HttpStatus.OK)
.body(channelService.update(dto));
}

@RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
public ResponseEntity<?> delete(@PathVariable UUID id) {
@DeleteMapping(value = "/{id}")
public ResponseEntity<Void> delete(@PathVariable UUID id) {
channelService.delete(id);
return ResponseEntity.status(204).body("Channel is removed");
return ResponseEntity.status(HttpStatus.NO_CONTENT).build();
}
}
Loading