Skip to content

Commit

Permalink
Merge pull request #1 from DDD-Community/feature/POLABO-32
Browse files Browse the repository at this point in the history
feat(POLABO-32): 1차 MVP 기능 개발
  • Loading branch information
dldmsql authored Jun 29, 2024
2 parents fcf125d + 13f533b commit 49737df
Show file tree
Hide file tree
Showing 34 changed files with 907 additions and 9 deletions.
60 changes: 60 additions & 0 deletions .github/workflows/cd-dev.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
name: Java CI with Gradle

on:
pull_request:
branches: [ "dev" ]

jobs:
build:
## checkout후 자바 21 버전으로 설정을 합니다
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'
## gradlew 의 권한을 줍니다.
- name: Grant execute permission for gradlew
run: chmod +x gradlew

## gradle build
- name: Build with Gradle
run: ./gradlew clean build --debug
env:
DB_URL: ${{ secrets.DB_URL }}
DB_USER: ${{ secrets.DB_USER }}
DB_PASSWORD: ${{ secrets.DB_PASSWORD }}
JASYPT_ENCRYPTOR_PASSWORD: ${{ secrets.JASYPT_ENCRYPTOR_PASSWORD }}

## 이미지 태그에 시간 설정을 하기위해서 현재 시간을 가져옵니다.
- name: Get current time
uses: 1466587594/get-current-time@v2
id: current-time
with:
format: YYYY-MM-DDTHH-mm-ss
utcOffset: "+09:00"

- name: Show Current Time
run: echo "CurrentTime=${{steps.current-time.outputs.formattedTime}}"
## AWS에 로그인. aws-region은 서울로 설정(ap-northeast-2)
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ap-northeast-2
## ECR에 로그인
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v1
## sample라는 ECR 리파지터리에 현재 시간 태그를 생성하고, 푸쉬
## 앞의 스탭에서 ${{steps.current-time.outputs.formattedTime}}로 현재 시간을 가져옵니다.
- name: Build, tag, and push image to Amazon ECR
run: |
docker build --build-arg PASSWORD=$PASSWORD -t polabo:${{steps.current-time.outputs.formattedTime}} .
docker tag polabo:${{steps.current-time.outputs.formattedTime}} 058264417437.dkr.ecr.ap-northeast-2.amazonaws.com/polabo:${{steps.current-time.outputs.formattedTime}}
docker push 058264417437.dkr.ecr.ap-northeast-2.amazonaws.com/polabo:${{steps.current-time.outputs.formattedTime}}
env:
PASSWORD: ${{ secrets.JASYPT_ENCRYPTOR_PASSWORD }}
6 changes: 6 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
FROM openjdk:21
ARG JAR_FILE=./build/libs/*SNAPSHOT.jar
ARG PASSWORD
COPY ${JAR_FILE} polabo.jar
ENV JASYPT_ENCRYPTOR_PASSWORD=${PASSWORD}
ENTRYPOINT ["sh", "-c", "java ${JAVA_OPTS} ${ENVIRONMENT_VALUE} -jar /polabo.jar.jar", "-Djasypt.encryptor.password=${JASYPT_ENCRYPTOR_PASSWORD}"]
67 changes: 61 additions & 6 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import org.jetbrains.kotlin.gradle.idea.proto.com.google.protobuf.GeneratedCodeInfoKt.annotation
import nu.studer.gradle.jooq.JooqEdition
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
val kotlinVersion = "1.9.24"
Expand All @@ -9,14 +10,52 @@ plugins {
kotlin("plugin.jpa") version kotlinVersion
kotlin("plugin.allopen") version kotlinVersion
kotlin("kapt") version kotlinVersion
id("nu.studer.jooq") version "9.0"
}

group = "com.ddd"
version = "0.0.1-SNAPSHOT"

java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
languageVersion.set(JavaLanguageVersion.of(21))
}
}

jooq {
version.set("3.18.10")
edition.set(JooqEdition.OSS)

configurations {
create("main") {
generateSchemaSourceOnCompilation.set(true)
jooqConfiguration.apply {
logging = org.jooq.meta.jaxb.Logging.WARN
jdbc.apply {
driver = "com.mysql.cj.jdbc.Driver"
url = System.getenv("DB_URL") ?: "jdbc:mysql://localhost:3306/polabo"
user = System.getenv("DB_USER") ?: "polabo"
password = System.getenv("DB_PASSWORD") ?: "polabo"
}
generator.apply {
name = "org.jooq.codegen.KotlinGenerator"
database.apply {
name = "org.jooq.meta.mysql.MySQLDatabase"
excludes = "sys"
}
generate.apply {
isDeprecated = false
isFluentSetters = true
isRecords = true
}
target.apply {
packageName = "com.ddd.sonnypolabobe.jooq"
directory = "build/generated-src/jooq/main"
}
strategy.name = "org.jooq.codegen.DefaultGeneratorStrategy"
}
}
}
}
}

Expand All @@ -34,20 +73,36 @@ dependencies {
implementation("org.springframework.boot:spring-boot-starter")
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.2.0")
// runtimeOnly("org.mariadb.jdbc:mariadb-java-client")
// implementation("org.springframework.boot:spring-boot-starter-data-jpa")
implementation("org.springframework.boot:spring-boot-starter-webflux")
implementation("org.springframework.boot:spring-boot-starter-actuator")
implementation("org.jetbrains.kotlin:kotlin-reflect")
runtimeOnly("com.mysql:mysql-connector-j")
implementation("org.springframework.boot:spring-boot-starter-jooq")
jooqGenerator("com.mysql:mysql-connector-j")
jooqGenerator("org.jooq:jooq-meta:3.18.10")
jooqGenerator("org.jooq:jooq-codegen:3.18.10")
testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("org.jetbrains.kotlin:kotlin-test-junit5")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
implementation ("com.github.f4b6a3:uuid-creator:5.3.3")
implementation("software.amazon.awssdk:s3:2.20.68")
implementation("com.amazonaws:aws-java-sdk-s3:1.12.561")
implementation("com.github.ulisesbocchio:jasypt-spring-boot-starter:3.0.5")

}

kotlin {
compilerOptions {
freeCompilerArgs.addAll("-Xjsr305=strict")
tasks.withType<KotlinCompile> {
kotlinOptions {
freeCompilerArgs += "-Xjsr305=strict"
jvmTarget = "21"
}
}

tasks.withType<Test> {
useJUnitPlatform()
}

tasks.withType<Jar> {
enabled = false
}
26 changes: 26 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
version: '3.7'

services:
mysql:
image: mysql:latest
container_name: polabo_mysql
hostname: polabo_mysql
volumes:
- ./mysqldata:/var/lib/mysql
environment:
- MYSQL_USER=polabo
- MYSQL_PASSWORD=polabo
- MYSQL_ROOT_PASSWORD=polabo
- MYSQL_HOST=localhost
- MYSQL_PORT=3306
- MYSQL_DATABASE=polabo
ports:
- "3306:3306"


# redis:
# image: redis
# container_name: polabo_redis
# hostname: polabo_redis
# ports:
# - "6379:6379"
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
zipStorePath=wrapper/dists
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package com.ddd.sonnypolabobe

import org.slf4j.LoggerFactory
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication

@SpringBootApplication
class SonnyPolaboBeApplication

inline fun <reified T> T.logger() = LoggerFactory.getLogger(T::class.java)!!
fun main(args: Array<String>) {
runApplication<SonnyPolaboBeApplication>(*args)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.ddd.sonnypolabobe.domain.board.controller

import com.ddd.sonnypolabobe.domain.board.controller.dto.BoardCreateRequest
import com.ddd.sonnypolabobe.domain.board.controller.dto.BoardGetResponse
import com.ddd.sonnypolabobe.domain.board.service.BoardService
import com.ddd.sonnypolabobe.global.response.ApplicationResponse
import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.tags.Tag
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
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

@Tag(name = "Board API", description = "보드 관련 API")
@RestController
@RequestMapping("/api/v1/boards")
class BoardController(
private val boardService: BoardService
) {
@Operation(summary = "보드 생성", description = """
보드를 생성합니다.
userId는 추후 회원가입 기능이 추가될 것을 대비한 것입니다. 지금은 null로 주세요.
""")
@PostMapping
fun create(@RequestBody request : BoardCreateRequest)
= ApplicationResponse.ok(this.boardService.create(request))

@Operation(summary = "보드 조회", description = """
보드를 조회합니다.
""")
@GetMapping("/{id}")
fun get(@PathVariable id : String)
= ApplicationResponse.ok(this.boardService.getById(id))

@Operation(summary = "보드 누적 생성 수 조회", description = """
보드 누적 생성 수를 조회합니다.
""")
@GetMapping("/total-count")
fun getTotalCount() = ApplicationResponse.ok(this.boardService.getTotalCount())
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.ddd.sonnypolabobe.domain.board.controller.dto

import io.swagger.v3.oas.annotations.media.Schema
import jakarta.validation.constraints.NotBlank
import jakarta.validation.constraints.Pattern
import java.util.*

data class BoardCreateRequest(
@Schema(description = "제목", example = "쏘니의 보드")
@field:NotBlank
@field:Pattern(regexp = "^(?=.*[a-zA-Z])(?=.*[0-9])(?=.*[!@#$%^&*()_+=-])(?=.*[ㄱ-ㅎㅏ-ㅣ가-힣]).{1,20}$", message = "제목은 국문, 영문, 숫자, 특수문자, 띄어쓰기를 포함한 20자 이내여야 합니다.")
val title: String,
@Schema(description = "작성자 아이디", example = "null", required = false)
val userId: UUID? = null
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.ddd.sonnypolabobe.domain.board.controller.dto

import com.ddd.sonnypolabobe.domain.polaroid.controller.dto.PolaroidGetResponse
import io.swagger.v3.oas.annotations.media.Schema

data class BoardGetResponse(
@Schema(description = "제목", example = "쏘니의 보드")
val title: String,
@Schema(description = "작성자", example = "작성자입니다.")
val items: List<PolaroidGetResponse>
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.ddd.sonnypolabobe.domain.board.entity

import com.ddd.sonnypolabobe.global.entity.BaseEntity
import com.ddd.sonnypolabobe.global.util.UuidGenerator
import java.time.LocalDateTime
import java.util.*

class BoardEntity() : BaseEntity {
override val id: UUID = UuidGenerator.create()
var title: String = ""
var userId : UUID? = null
override var yn: Boolean = true
override val createdAt: LocalDateTime = LocalDateTime.now()
override var updatedAt: LocalDateTime = LocalDateTime.now()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.ddd.sonnypolabobe.domain.board.repository

import com.ddd.sonnypolabobe.domain.board.controller.dto.BoardCreateRequest
import org.jooq.Record6
import java.time.LocalDateTime
import java.util.*

interface BoardJooqRepository {
fun insertOne(request: BoardCreateRequest): ByteArray?
fun selectOneById(id: UUID) : Array<out Record6<String?, Long?, String?, String?, LocalDateTime?, ByteArray?>>
fun selectTotalCount(): Long
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package com.ddd.sonnypolabobe.domain.board.repository

import com.ddd.sonnypolabobe.domain.board.controller.dto.BoardCreateRequest
import com.ddd.sonnypolabobe.domain.board.controller.dto.BoardGetResponse
import com.ddd.sonnypolabobe.global.util.UuidConverter
import com.ddd.sonnypolabobe.global.util.UuidGenerator
import com.ddd.sonnypolabobe.jooq.polabo.tables.Board
import com.ddd.sonnypolabobe.jooq.polabo.tables.Polaroid
import org.jooq.DSLContext
import org.jooq.Record6
import org.springframework.stereotype.Repository
import java.time.LocalDateTime
import java.util.*

@Repository
class BoardJooqRepositoryImpl(
private val dslContext: DSLContext
) : BoardJooqRepository {
override fun insertOne(request: BoardCreateRequest): ByteArray? {
val jBoard = Board.BOARD
val id = UuidConverter.uuidToByteArray(UuidGenerator.create())
val insertValue = jBoard.newRecord().apply {
this.id = id
this.title = request.title
this.createdAt = LocalDateTime.now()
this.yn = 1
this.activeyn = 1
}
val result = this.dslContext.insertInto(jBoard)
.set(insertValue)
.execute()

return if (result == 1) id else null
}

override fun selectOneById(id: UUID): Array<out Record6<String?, Long?, String?, String?, LocalDateTime?, ByteArray?>> {
val jBoard = Board.BOARD
val jPolaroid = Polaroid.POLAROID
return this.dslContext
.select(
jBoard.TITLE,
jPolaroid.ID,
jPolaroid.IMAGE_KEY,
jPolaroid.ONE_LINE_MESSAGE,
jPolaroid.CREATED_AT,
jPolaroid.USER_ID
)
.from(jBoard)
.leftJoin(jPolaroid).on(
jBoard.ID.eq(jPolaroid.BOARD_ID).and(jPolaroid.YN.eq(1))
.and(jPolaroid.ACTIVEYN.eq(1))
)
.where(
jBoard.ID.eq(UuidConverter.uuidToByteArray(id)).and(jBoard.YN.eq(1))
.and(jBoard.ACTIVEYN.eq(1))
)
.fetchArray()

}

override fun selectTotalCount(): Long {
val jBoard = Board.BOARD
return this.dslContext
.selectCount()
.from(jBoard)
.where(jBoard.YN.eq(1).and(jBoard.ACTIVEYN.eq(1)))
.fetchOne(0, Long::class.java) ?: 0
}
}
Loading

0 comments on commit 49737df

Please sign in to comment.