Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
01ae498
init: 프로젝트 생성
wonu606 Oct 4, 2023
be1f984
feat: UrlShortener 기본 페이지 구현
wonu606 Oct 4, 2023
dbe64d9
test: UrlShortenerApiController 테스트 코드 작성
wonu606 Oct 6, 2023
4a6cfa6
refactor: UrlShortenerApiController 테스트 코드에 맞게 구현 수정
wonu606 Oct 6, 2023
97190ed
chore: mapstruct 의존성 추가
wonu606 Oct 6, 2023
5e26286
feat: UrlShortenerService 추상화
wonu606 Oct 6, 2023
67a0143
refactor: URL 생성시 최소 1글자이상 입력하도록 변경
wonu606 Oct 7, 2023
8b5a7e4
test: UrlShortenerApiController MockTest 추가 및 코드 가독성 개선
wonu606 Oct 7, 2023
2ea86e8
refactor: URL 입력시 길이 제한 및 ControllerMapper null 방지
wonu606 Oct 7, 2023
74baac5
refactor: DefaultUrlShortenerService 로직 구현 및 UrlLink 도메인 구현
wonu606 Oct 7, 2023
f8a527c
test: UrlLink 도메인 테스트 작성
wonu606 Oct 8, 2023
7f6f6dd
fix: UrlLink 도메인에 검증 추가
wonu606 Oct 8, 2023
e28ee37
style: HashedUrl 클래스명을 UrlHash로 변경
wonu606 Oct 8, 2023
561239b
feat: 유일한 Url Hash를 생성 기능 구현
wonu606 Oct 8, 2023
d61f2e3
test: UniqueUrlHashCreator 클래스에 대한 테스트 작성
wonu606 Oct 8, 2023
7356510
chore: 테스트용 application.properties 오류 수정
wonu606 Oct 8, 2023
9bd2e13
style: 테스트 내부 클래명 통일
wonu606 Oct 8, 2023
293beec
feat: URL 해싱 알고리즘 구현
wonu606 Oct 8, 2023
05e4295
refactor: Url 클래스 생성 시 길이 검증 로직 추가
wonu606 Oct 8, 2023
389bc98
test: `ChunkBasedUrlHashCreator` 클래스에 대한 테스트 코드 작성
wonu606 Oct 8, 2023
3da58f7
feat: `UrlLink`를 Local Memory에 저장하는 기능 구현
wonu606 Oct 8, 2023
3a5eaff
test: `DefaultShortenerService` 테스트 코드 작성 및 통과
wonu606 Oct 9, 2023
ca76b59
style: UrlShortenerApiController와 UrlShortenerService의 create 함수명 통일
wonu606 Oct 9, 2023
4831fa2
refactor: 기존의 ShortenURL이 존재할 경우 새로 생성하지 않고 반환하도록 수정
wonu606 Oct 9, 2023
c1a2050
test: 기존의 ShortenURL이 존재할 경우 새로 생성하지 않고 반환하는 테스트 코드 작성 및 통과
wonu606 Oct 9, 2023
201eb19
test: 불필요한 테스트 클래스 제거 및 코드 컨벤션에 맞게 수정
wonu606 Oct 9, 2023
82f326a
style: Controller, Service 메소드명 로직에 맞게 수정
wonu606 Oct 9, 2023
1da2f9f
feat: ShortUrl을 이용한 redirect 기능 구현
wonu606 Oct 9, 2023
a68ddf4
test: ShortUrl을 통한 redirect 테스트 작성 및 실패
wonu606 Oct 9, 2023
64ecb81
fix: `DefaultUrlShortenerService`에서 ShortUrl 조회 시 원본 URL 반환 로직 수정
wonu606 Oct 9, 2023
1b66b5d
feat: Short Url 생성 페이지에 카피 기능 추가
wonu606 Oct 9, 2023
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
183 changes: 183 additions & 0 deletions short-url/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
# Created by https://www.toptal.com/developers/gitignore/api/macos,intellij+iml,java,gradle
# Edit at https://www.toptal.com/developers/gitignore?templates=macos,intellij+iml,java,gradle

### Intellij+iml ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839

# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf

# AWS User-specific
.idea/**/aws.xml

# Generated files
.idea/**/contentModel.xml

# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml

# Gradle
.idea/**/gradle.xml
.idea/**/libraries

# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/artifacts
# .idea/compiler.xml
# .idea/jarRepositories.xml
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# *.iml
# *.ipr

# CMake
cmake-build-*/

# Mongo Explorer plugin
.idea/**/mongoSettings.xml

# File-based project format
*.iws

# IntelliJ
out/

# mpeltonen/sbt-idea plugin
.idea_modules/

# JIRA plugin
atlassian-ide-plugin.xml

# Cursive Clojure plugin
.idea/replstate.xml

# SonarLint plugin
.idea/sonarlint/

# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties

# Editor-based Rest Client
.idea/httpRequests

# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser

### Intellij+iml Patch ###
# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023

*.iml
modules.xml
.idea/misc.xml
*.ipr

### Java ###
# Compiled class file
*.class

# Log file
*.log

# BlueJ files
*.ctxt

# Mobile Tools for Java (J2ME)
.mtj.tmp/

# Package Files #
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar

# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
replay_pid*

### macOS ###
# General
.DS_Store
.AppleDouble
.LSOverride

# Icon must end with two \r
Icon


# Thumbnails
._*

# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent

# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk

### macOS Patch ###
# iCloud generated files
*.icloud

### Gradle ###
.gradle
**/build/
!src/**/build/

# Ignore Gradle GUI config
gradle-app.setting

# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
!gradle-wrapper.jar

# Avoid ignore Gradle wrappper properties
!gradle-wrapper.properties

# Cache of project
.gradletasknamecache

# Eclipse Gradle plugin generated files
# Eclipse Core
.project
# JDT-specific (Eclipse Java Development Tools)
.classpath

### Gradle Patch ###
# Java heap dump
*.hprof

## added files
gradlew*
HELP.md
.idea

# End of https://www.toptal.com/developers/gitignore/api/macos,intellij+iml,java,gradle

58 changes: 58 additions & 0 deletions short-url/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
plugins {
id 'java'
id 'org.springframework.boot' version '3.1.4'
id 'io.spring.dependency-management' version '1.1.3'
id 'org.asciidoctor.jvm.convert' version '3.3.2'
}

group = 'com.prgrms.wonu606'
version = '0.0.1-SNAPSHOT'

java {
sourceCompatibility = '17'
}

configurations {
compileOnly {
extendsFrom annotationProcessor
}
}

repositories {
mavenCentral()
}

ext {
set('snippetsDir', file("build/generated-snippets"))
}

dependencies {
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation ('commons-validator:commons-validator:1.7') {
exclude group: 'commons-collections', module: 'commons-collections'
}
implementation 'org.apache.commons:commons-collections4:4.4'
implementation 'org.apache.commons:commons-lang3:3.0'
implementation 'org.mapstruct:mapstruct:1.5.5.Final'

compileOnly 'org.projectlombok:lombok'
compileOnly 'org.mapstruct:mapstruct-processor:1.5.5.Final'

annotationProcessor 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok-mapstruct-binding:0.2.0'
annotationProcessor 'org.mapstruct:mapstruct-processor:1.5.5.Final'

testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

tasks.named('test') {
outputs.dir snippetsDir
useJUnitPlatform()
}

tasks.named('asciidoctor') {
inputs.dir snippetsDir
dependsOn test
}
Binary file added short-url/gradle/wrapper/gradle-wrapper.jar
Binary file not shown.
7 changes: 7 additions & 0 deletions short-url/gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
1 change: 1 addition & 0 deletions short-url/settings.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
rootProject.name = 'short-url'
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.prgrms.wonu606.shorturl;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class ShortUrlApplication {

public static void main(String[] args) {
SpringApplication.run(ShortUrlApplication.class, args);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.prgrms.wonu606.shorturl.controller;

import com.prgrms.wonu606.shorturl.service.UrlShortenerService;
import java.net.URI;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequiredArgsConstructor
public class ShortUrlApiController {

private final UrlShortenerService urlShortenerService;

@GetMapping("/{shortUrl}")
public ResponseEntity<Void> redirect(@PathVariable String shortUrl) {
String originalUrl = urlShortenerService.getOriginalUrlByShortUrl(shortUrl);

return ResponseEntity
.status(HttpStatus.FOUND)
.location(URI.create(originalUrl))
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package com.prgrms.wonu606.shorturl.controller;

import com.prgrms.wonu606.shorturl.controller.dto.ShortenUrlCreateRequest;
import com.prgrms.wonu606.shorturl.controller.dto.ShortenUrlCreateResponse;
import com.prgrms.wonu606.shorturl.controller.mapper.UrlShortenerApiParamMapper;
import com.prgrms.wonu606.shorturl.controller.mapper.UrlShortenerApiResponseMapper;
import com.prgrms.wonu606.shorturl.service.UrlShortenerService;
import com.prgrms.wonu606.shorturl.service.dto.ShortenUrlCreateParam;
import com.prgrms.wonu606.shorturl.service.dto.ShortenUrlCreateResult;
import jakarta.validation.Valid;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
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("/api/shorten-url")
public class UrlShortenerApiController {

private final UrlShortenerService urlShortenerService;
private final UrlShortenerApiParamMapper paramMapper;
private final UrlShortenerApiResponseMapper responseMapper;
private final String baseUrl;

public UrlShortenerApiController(
UrlShortenerService urlShortenerService,
UrlShortenerApiParamMapper paramMapper,
UrlShortenerApiResponseMapper responseMapper,
@Value("${url-shortener.base-url}") String baseUrl) {
this.urlShortenerService = urlShortenerService;
this.paramMapper = paramMapper;
this.responseMapper = responseMapper;
this.baseUrl = baseUrl;
}

@PostMapping(
produces = MediaType.APPLICATION_JSON_VALUE,
consumes = MediaType.APPLICATION_JSON_VALUE
)
public ResponseEntity<ShortenUrlCreateResponse> findOrCreateShortenedUrl(
@RequestBody @Valid ShortenUrlCreateRequest request) {
ShortenUrlCreateParam param = paramMapper.toShortenUrlCreateParam(request);

ShortenUrlCreateResult result = urlShortenerService.findOrCreateShortenUrlHash(param);

HttpStatus httpStatus = result.isNew() ? HttpStatus.CREATED : HttpStatus.OK;
return ResponseEntity
.status(httpStatus)
.body(responseMapper.toShortenUrlCreateResponse(result, baseUrl));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.prgrms.wonu606.shorturl.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class UrlShortenerController {

@GetMapping("/")
public String displayIndexPage() {
return "index";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.prgrms.wonu606.shorturl.controller.dto;

import jakarta.validation.constraints.NotBlank;
import org.hibernate.validator.constraints.Length;

public record ShortenUrlCreateRequest(
@NotBlank(message = "URL은 null이거나 공백일 수 없습니다.")
@Length(max = 2000, message = "URL 길이는 2000자를 넘길 수 없습니다.")
String originalUrl) {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.prgrms.wonu606.shorturl.controller.dto;

public record ShortenUrlCreateResponse(String shortenUrl) {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.prgrms.wonu606.shorturl.controller.mapper;

import com.prgrms.wonu606.shorturl.controller.dto.ShortenUrlCreateRequest;
import com.prgrms.wonu606.shorturl.service.dto.ShortenUrlCreateParam;
import org.mapstruct.Mapper;
import org.mapstruct.ReportingPolicy;

@Mapper(
componentModel = "spring",
unmappedTargetPolicy = ReportingPolicy.ERROR
)
public interface UrlShortenerApiParamMapper {

ShortenUrlCreateParam toShortenUrlCreateParam(ShortenUrlCreateRequest request);
}
Loading