Compare commits

4 Commits

23 changed files with 319 additions and 3 deletions

View File

@@ -3,6 +3,7 @@ plugins {
kotlin("plugin.spring") version "2.2.21" kotlin("plugin.spring") version "2.2.21"
id("org.springframework.boot") version "4.0.0" id("org.springframework.boot") version "4.0.0"
id("io.spring.dependency-management") version "1.1.7" id("io.spring.dependency-management") version "1.1.7"
kotlin("plugin.jpa") version "2.2.21"
} }
group = "net.halfbinary" group = "net.halfbinary"
@@ -26,16 +27,17 @@ repositories {
} }
dependencies { dependencies {
val mysqlConnectorJ = "9.5.0"
implementation("org.springframework.boot:spring-boot-starter-actuator") implementation("org.springframework.boot:spring-boot-starter-actuator")
implementation("org.springframework.boot:spring-boot-starter-webmvc") implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springframework.boot:spring-boot-starter-data-jpa") implementation("org.springframework.boot:spring-boot-starter-data-jpa")
implementation("com.mysql:mysql-connector-j:${mysqlConnectorJ}")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin") implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
implementation("org.jetbrains.kotlin:kotlin-reflect") implementation("org.jetbrains.kotlin:kotlin-reflect")
developmentOnly("org.springframework.boot:spring-boot-devtools") developmentOnly("org.springframework.boot:spring-boot-devtools")
runtimeOnly("com.h2database:h2")
annotationProcessor("org.springframework.boot:spring-boot-configuration-processor") annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")
testImplementation("org.springframework.boot:spring-boot-starter-actuator-test") testImplementation("org.springframework.boot:spring-boot-starter-actuator-test")
testImplementation("org.springframework.boot:spring-boot-starter-webmvc-test") testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("org.jetbrains.kotlin:kotlin-test-junit5") testImplementation("org.jetbrains.kotlin:kotlin-test-junit5")
testRuntimeOnly("org.junit.platform:junit-platform-launcher") testRuntimeOnly("org.junit.platform:junit-platform-launcher")
} }
@@ -46,6 +48,12 @@ kotlin {
} }
} }
allOpen {
annotation("jakarta.persistence.Entity")
annotation("jakarta.persistence.MappedSuperclass")
annotation("jakarta.persistence.Embeddable")
}
tasks.withType<Test> { tasks.withType<Test> {
useJUnitPlatform() useJUnitPlatform()
} }

View File

@@ -0,0 +1,16 @@
package net.halfbinary.scavengerhuntapi.controller
import net.halfbinary.scavengerhuntapi.model.converter.toDomain
import net.halfbinary.scavengerhuntapi.model.request.HunterSignupRequest
import net.halfbinary.scavengerhuntapi.service.SignupService
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RestController
@RestController
class SignupController(private val signupService: SignupService) {
@PostMapping("/signup")
fun hunterSignup(@RequestBody body: HunterSignupRequest) {
signupService.createNewHunter(body.toDomain())
}
}

View File

@@ -0,0 +1,8 @@
package net.halfbinary.scavengerhuntapi.model
enum class FoundStatus {
SUBMITTED,
APPROVED,
REJECTED,
REMOVED
}

View File

@@ -0,0 +1,9 @@
package net.halfbinary.scavengerhuntapi.model
import java.util.*
typealias FoundId = UUID
typealias HuntId = UUID
typealias HunterId = UUID
typealias ItemId = UUID
typealias TeamId = UUID

View File

@@ -0,0 +1,18 @@
package net.halfbinary.scavengerhuntapi.model.converter
import net.halfbinary.scavengerhuntapi.model.domain.Hunter
import net.halfbinary.scavengerhuntapi.model.record.HunterRecord
import net.halfbinary.scavengerhuntapi.model.request.HunterSignupRequest
fun HunterSignupRequest.toDomain(): Hunter {
return Hunter(
email = email,
name = name,
password = password,
isAdmin = false
)
}
fun Hunter.toRecord(): HunterRecord {
return HunterRecord(id, email, name, password, isAdmin)
}

View File

@@ -0,0 +1,11 @@
package net.halfbinary.scavengerhuntapi.model.domain
import java.util.UUID
data class Hunter(
val id: UUID = UUID.randomUUID(),
val email: String,
val name: String,
val password: String,
val isAdmin: Boolean
)

View File

@@ -0,0 +1,23 @@
package net.halfbinary.scavengerhuntapi.model.record
import jakarta.persistence.Entity
import jakarta.persistence.Id
import jakarta.persistence.Table
import net.halfbinary.scavengerhuntapi.model.*
import java.time.LocalDateTime
/**
* Represents a found Item for a Hunt by a Hunter
*/
@Entity
@Table(name = "found")
data class FoundRecord(
@Id
val id: FoundId,
val itemId: ItemId,
val huntId: HuntId,
val hunterId: HunterId,
val foundDateTime: LocalDateTime,
val imageName: String,
val status: FoundStatus
)

View File

@@ -0,0 +1,17 @@
package net.halfbinary.scavengerhuntapi.model.record
import jakarta.persistence.Entity
import jakarta.persistence.Id
import jakarta.persistence.Table
import net.halfbinary.scavengerhuntapi.model.HuntId
import net.halfbinary.scavengerhuntapi.model.ItemId
import java.util.*
@Entity
@Table(name = "hunt_item")
data class HuntItemRecord(
@Id
val id: UUID,
val huntId: HuntId,
val itemId: ItemId
)

View File

@@ -0,0 +1,22 @@
package net.halfbinary.scavengerhuntapi.model.record
import jakarta.persistence.Entity
import jakarta.persistence.Id
import jakarta.persistence.Table
import net.halfbinary.scavengerhuntapi.model.HuntId
import java.time.LocalDateTime
/**
* Represents a scavenger hunt event
* @property isTerminated Is the event prematurely stopped
*/
@Entity
@Table(name = "hunt")
data class HuntRecord(
@Id
val id: HuntId,
val title: String,
val startDateTime: LocalDateTime,
val endDateTime: LocalDateTime,
val isTerminated: Boolean
)

View File

@@ -0,0 +1,20 @@
package net.halfbinary.scavengerhuntapi.model.record
import jakarta.persistence.Entity
import jakarta.persistence.Id
import jakarta.persistence.Table
import net.halfbinary.scavengerhuntapi.model.HuntId
import net.halfbinary.scavengerhuntapi.model.HunterId
import java.util.*
/**
* Connects a Hunter to a Hunt as a judge
*/
@Entity
@Table(name = "hunter_hunt")
data class HunterHuntRecord(
@Id
val id: UUID,
val hunterId: HunterId,
val huntId: HuntId
)

View File

@@ -0,0 +1,21 @@
package net.halfbinary.scavengerhuntapi.model.record
import jakarta.persistence.Entity
import jakarta.persistence.Id
import jakarta.persistence.Table
import net.halfbinary.scavengerhuntapi.model.HunterId
/**
* Represents a user
* @property isAdmin Is a site administrator
*/
@Entity
@Table(name = "hunter")
data class HunterRecord(
@Id
val id: HunterId,
val email: String,
val name: String,
val password: String,
val isAdmin: Boolean
)

View File

@@ -0,0 +1,17 @@
package net.halfbinary.scavengerhuntapi.model.record
import jakarta.persistence.Entity
import jakarta.persistence.Id
import jakarta.persistence.Table
import net.halfbinary.scavengerhuntapi.model.HunterId
import net.halfbinary.scavengerhuntapi.model.TeamId
import java.util.*
@Entity
@Table(name = "hunter_team")
data class HunterTeamRecord(
@Id
val id: UUID,
val hunterId: HunterId,
val teamId: TeamId
)

View File

@@ -0,0 +1,18 @@
package net.halfbinary.scavengerhuntapi.model.record
import jakarta.persistence.Entity
import jakarta.persistence.Id
import jakarta.persistence.Table
import net.halfbinary.scavengerhuntapi.model.ItemId
/**
* Represents an item to be found on a Hunt
*/
@Entity
@Table(name = "item")
data class ItemRecord(
@Id
val id: ItemId,
val name: String,
val points: Int
)

View File

@@ -0,0 +1,17 @@
package net.halfbinary.scavengerhuntapi.model.record
import jakarta.persistence.Entity
import jakarta.persistence.Id
import jakarta.persistence.Table
import net.halfbinary.scavengerhuntapi.model.HuntId
import net.halfbinary.scavengerhuntapi.model.TeamId
import java.util.*
@Entity
@Table(name = "team_hunt")
data class TeamHuntRecord(
@Id
val id: UUID,
val teamId: TeamId,
val huntId: HuntId
)

View File

@@ -0,0 +1,17 @@
package net.halfbinary.scavengerhuntapi.model.record
import jakarta.persistence.Entity
import jakarta.persistence.Id
import jakarta.persistence.Table
import net.halfbinary.scavengerhuntapi.model.TeamId
/**
* Represents a group (1+) of people hunting on a Hunt
*/
@Entity
@Table(name = "team")
data class TeamRecord(
@Id
val id: TeamId,
val name: String,
)

View File

@@ -0,0 +1,7 @@
package net.halfbinary.scavengerhuntapi.model.request
data class HunterSignupRequest(
val email: String,
val name: String,
val password: String
)

View File

@@ -0,0 +1,9 @@
package net.halfbinary.scavengerhuntapi.repository
import net.halfbinary.scavengerhuntapi.model.FoundId
import net.halfbinary.scavengerhuntapi.model.record.FoundRecord
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.stereotype.Repository
@Repository
interface FoundRepository : JpaRepository<FoundRecord, FoundId>

View File

@@ -0,0 +1,9 @@
package net.halfbinary.scavengerhuntapi.repository
import net.halfbinary.scavengerhuntapi.model.HuntId
import net.halfbinary.scavengerhuntapi.model.record.HuntRecord
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.stereotype.Repository
@Repository
interface HuntRepository : JpaRepository<HuntRecord, HuntId>

View File

@@ -0,0 +1,9 @@
package net.halfbinary.scavengerhuntapi.repository
import net.halfbinary.scavengerhuntapi.model.HunterId
import net.halfbinary.scavengerhuntapi.model.record.HunterRecord
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.stereotype.Repository
@Repository
interface HunterRepository : JpaRepository<HunterRecord, HunterId>

View File

@@ -0,0 +1,9 @@
package net.halfbinary.scavengerhuntapi.repository
import net.halfbinary.scavengerhuntapi.model.ItemId
import net.halfbinary.scavengerhuntapi.model.record.ItemRecord
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.stereotype.Repository
@Repository
interface ItemRepository : JpaRepository<ItemRecord, ItemId>

View File

@@ -0,0 +1,9 @@
package net.halfbinary.scavengerhuntapi.repository
import net.halfbinary.scavengerhuntapi.model.TeamId
import net.halfbinary.scavengerhuntapi.model.record.TeamRecord
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.stereotype.Repository
@Repository
interface TeamRepository : JpaRepository<TeamRecord, TeamId>

View File

@@ -0,0 +1,13 @@
package net.halfbinary.scavengerhuntapi.service
import net.halfbinary.scavengerhuntapi.model.converter.toRecord
import net.halfbinary.scavengerhuntapi.model.domain.Hunter
import net.halfbinary.scavengerhuntapi.repository.HunterRepository
import org.springframework.stereotype.Service
@Service
class SignupService(private val hunterRepository: HunterRepository) {
fun createNewHunter(hunter: Hunter) {
hunterRepository.save(hunter.toRecord())
}
}

View File

@@ -1 +1,10 @@
spring.application.name=scavengerhuntapi spring.application.name=scavengerhuntapi
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.type.preferred_uuid_jdbc_type=CHAR
spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver
spring.datasource.url=${DB_URL}
spring.datasource.username=${DB_USER}
spring.datasource.password=${DB_PASSWORD}