Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ package simplerag.ragback.domain.prompt.controller
import jakarta.validation.Valid
import org.springframework.http.HttpStatus
import org.springframework.web.bind.annotation.*
import simplerag.ragback.domain.prompt.dto.PromptCreateRequest
import simplerag.ragback.domain.prompt.dto.PromptPreviewResponse
import simplerag.ragback.domain.index.dto.IndexUpdateRequest
import simplerag.ragback.domain.prompt.dto.*
import simplerag.ragback.domain.prompt.service.PromptService
import simplerag.ragback.global.response.ApiResponse

Expand All @@ -23,4 +23,38 @@ class PromptController(
return ApiResponse.ok(savedPrompt)
}

@GetMapping("/{promptId}")
fun getPrompt(
@PathVariable promptId: Long,
): ApiResponse<PromptDetailResponse> {
val promptDetail = promptService.getPrompt(promptId)
return ApiResponse.ok(promptDetail)
}

@GetMapping
fun getPrompts(
@RequestParam cursor: Long,
@RequestParam take: Int,
): ApiResponse<PromptPreviewResponseList> {
val promptPreviewResponseList = promptService.getPrompts(cursor, take)
return ApiResponse.ok(promptPreviewResponseList)
}
Comment on lines +34 to +41
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add sane defaults and validation for cursor/take; enable @validated

Without defaults, the list endpoint 400s unless both params are provided. Also guard against negative cursor and unbounded take. You’ll need @validated on the controller class for @Min/@max on @RequestParam to take effect.

Apply this diff to the parameter annotations:

-    fun getPrompts(
-        @RequestParam cursor: Long,
-        @RequestParam take: Int,
-    ): ApiResponse<PromptPreviewResponseList> {
+    fun getPrompts(
+        @RequestParam(defaultValue = "0") @jakarta.validation.constraints.Min(0) cursor: Long,
+        @RequestParam(defaultValue = "20") @jakarta.validation.constraints.Min(1) @jakarta.validation.constraints.Max(100) take: Int,
+    ): ApiResponse<PromptPreviewResponseList> {

Add @validated on the controller (outside selected range) so the @RequestParam constraints are enforced:

import org.springframework.validation.annotation.Validated

@Validated
@RestController
@RequestMapping("/api/v1/prompts")
class PromptController(
    private val promptService: PromptService
) { ... }


@PutMapping("/{promptId}")
fun updatePrompt(
@RequestBody @Valid promptUpdateRequest: PromptUpdateRequest,
@PathVariable promptId: Long,
): ApiResponse<PromptPreviewResponse> {
val updatedPrompt = promptService.updatePrompt(promptUpdateRequest, promptId)
return ApiResponse.ok(updatedPrompt)
}
Comment on lines +43 to +50
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Confirm update rules for non-CUSTOM presets and test them

Ensure the service/domain layer:

  • rejects systemPrompt changes when preSet != CUSTOM (throw PromptException with CUSTOM_SYSTEM_PROMPT), and
  • sets/retains the correct defaultSystemPrompt when switching presets.

Add integration tests covering:

  1. CUSTOM → non-CUSTOM with systemPrompt provided (expect 400/PROMPT_001),
  2. non-CUSTOM → CUSTOM without systemPrompt (expect default or explicit rule),
  3. simple name-only updates.

I can generate test scaffolding for these scenarios if helpful.


@DeleteMapping("/{promptId}")
fun deletePrompt(
@PathVariable promptId: Long,
): ApiResponse<Unit> {
promptService.deletePrompt(promptId)
return ApiResponse.ok(null, "프롬프트가 삭제되었습니다.")
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,13 @@ data class PromptCreateRequest(
val name: String,
val preSet: PreSet,
@field:Size(max = 20000)
val systemPrompt: String,
val systemPrompt: String?,
)

data class PromptUpdateRequest(
@field:Size(max = 100)
val name: String,
val preSet: PreSet,
@field:Size(max = 20000)
val systemPrompt: String?,
)
Comment on lines +15 to 21
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Harden request validation and keep DB length aligned

  • Consider enforcing non-blank names on update as well. Today an empty string passes @SiZe(max=100).
  • Verify the DB column length for systemPrompt matches 20,000 to avoid silent truncation or persistence errors.

Apply this diff to enforce non-blank names on update:

-    @field:Size(max = 100)
-    val name: String,
+    @field:NotBlank
+    @field:Size(max = 100)
+    val name: String,

Add the missing import (outside the selected range):

import jakarta.validation.constraints.NotBlank

Verify DB mapping length matches:

Also consider applying @notblank to PromptCreateRequest.name for parity with update.


🏁 Script executed:

#!/bin/bash
# Find the Prompt entity and check 'systemPrompt' column length mapping
rg -nP --type=kt -C3 '\bclass\s+Prompt\b|@Column|\bsystemPrompt\b' src/main/kotlin

Length of output: 111


I’ll re-run the validation search with a glob-based grep to catch all Kotlin files:

This will confirm whether the DB column mapping for systemPrompt actually sets a 20,000-character length.


🏁 Script executed:

#!/bin/bash
# Search for Prompt entity definition
rg -n -C3 -g '*.kt' 'class Prompt' src/main/kotlin

# Look for the systemPrompt field in Prompt
rg -n -C3 -g '*.kt' 'systemPrompt' src/main/kotlin

# Check any @Column annotations specifying length
rg -n -C3 -g '*.kt' '@Column.*length' src/main/kotlin

Length of output: 18126


Enforce non-blank names on both create/update and clarify systemPrompt storage

  • PromptRequestDTO.kt
    • Add @field:NotBlank above name in both PromptCreateRequest and PromptUpdateRequest to prevent empty strings
    • Include the import:

    import jakarta.validation.constraints.NotBlank

    • Keep the existing @field:Size(max = 100) to enforce the length limit

  • Prompt entity mapping (src/main/kotlin/simplerag/ragback/domain/prompt/entity/Prompt.kt)
    • Currently, systemPrompt is annotated with @Lob (no explicit length), so it’s stored as a CLOB with effectively unlimited size
    • If you want to cap at 20,000 characters instead, replace:

    @Column(name = "system_prompt", nullable = false)
    @Lob
    var systemPrompt: String,

    with:

    @Column(name = "system_prompt", length = 20000, nullable = false)
    var systemPrompt: String,

    (and remove @Lob)
    • Otherwise, confirm your database’s CLOB type supports your required maximum size

Original file line number Diff line number Diff line change
@@ -1,15 +1,49 @@
package simplerag.ragback.domain.prompt.dto

import simplerag.ragback.domain.prompt.entity.Prompt
import simplerag.ragback.domain.prompt.entity.enums.PreSet

data class PromptPreviewResponseList(
val promptPreviewResponseList: List<PromptPreviewResponse>,
val cursor: Long?,
val hasNext: Boolean
) {
companion object {
fun from(prompts: List<Prompt>, cursor: Long?, hasNext: Boolean): PromptPreviewResponseList =
PromptPreviewResponseList(
promptPreviewResponseList = prompts.map { prompt ->
PromptPreviewResponse.from(prompt)
},
cursor = cursor,
hasNext = hasNext,
)
}
}
Comment on lines +6 to +21
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Prefer explicit nextCursor naming and simpler list field; also simplify mapping.

"cursor" here represents the next cursor the client should use, not the current one. Rename it to nextCursor to avoid API ambiguity. Also, "promptPreviewResponseList" is verbose—"items" reads better. Lastly, the mapping can be simplified.

This is an API surface change; confirm consumers before applying.

 data class PromptPreviewResponseList(
-    val promptPreviewResponseList: List<PromptPreviewResponse>,
-    val cursor: Long?,
+    val items: List<PromptPreviewResponse>,
+    val nextCursor: Long?,
     val hasNext: Boolean
 ) {
     companion object {
-        fun from(prompts: List<Prompt>, cursor: Long?, hasNext: Boolean): PromptPreviewResponseList =
+        fun from(prompts: List<Prompt>, nextCursor: Long?, hasNext: Boolean): PromptPreviewResponseList =
             PromptPreviewResponseList(
-                promptPreviewResponseList = prompts.map { prompt ->
-                    PromptPreviewResponse.from(prompt)
-                },
-                cursor = cursor,
+                items = prompts.map(PromptPreviewResponse::from),
+                nextCursor = nextCursor,
                 hasNext = hasNext,
             )
     }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
data class PromptPreviewResponseList(
val promptPreviewResponseList: List<PromptPreviewResponse>,
val cursor: Long?,
val hasNext: Boolean
) {
companion object {
fun from(prompts: List<Prompt>, cursor: Long?, hasNext: Boolean): PromptPreviewResponseList =
PromptPreviewResponseList(
promptPreviewResponseList = prompts.map { prompt ->
PromptPreviewResponse.from(prompt)
},
cursor = cursor,
hasNext = hasNext,
)
}
}
data class PromptPreviewResponseList(
val items: List<PromptPreviewResponse>,
val nextCursor: Long?,
val hasNext: Boolean
) {
companion object {
fun from(prompts: List<Prompt>, nextCursor: Long?, hasNext: Boolean): PromptPreviewResponseList =
PromptPreviewResponseList(
items = prompts.map(PromptPreviewResponse::from),
nextCursor = nextCursor,
hasNext = hasNext,
)
}
}
🤖 Prompt for AI Agents
In src/main/kotlin/simplerag/ragback/domain/prompt/dto/PromptResponseDTO.kt
around lines 6-21, rename the "cursor" property to "nextCursor" and the
"promptPreviewResponseList" field to the shorter "items", update the companion
factory signature to from(prompts: List<Prompt>, nextCursor: Long?, hasNext:
Boolean), simplify the mapping to prompts.map(PromptPreviewResponse::from) when
building the DTO, and update all call sites/consumers to the new property names
(confirm consumers before applying this API change).


data class PromptPreviewResponse(
val id: Long,
val name: String,
) {
companion object {
fun from(
prompt: Prompt
): PromptPreviewResponse {
return PromptPreviewResponse(prompt.id)
return PromptPreviewResponse(prompt.id, prompt.name)
}
}
Comment on lines 23 to +33
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Use expression body for compact mapping.

Minor readability nit: expression body is concise and idiomatic.

     companion object {
-        fun from(
-            prompt: Prompt
-        ): PromptPreviewResponse {
-            return PromptPreviewResponse(prompt.id, prompt.name)
-        }
+        fun from(prompt: Prompt): PromptPreviewResponse =
+            PromptPreviewResponse(prompt.id, prompt.name)
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
data class PromptPreviewResponse(
val id: Long,
val name: String,
) {
companion object {
fun from(
prompt: Prompt
): PromptPreviewResponse {
return PromptPreviewResponse(prompt.id)
return PromptPreviewResponse(prompt.id, prompt.name)
}
}
data class PromptPreviewResponse(
val id: Long,
val name: String,
) {
companion object {
fun from(prompt: Prompt): PromptPreviewResponse =
PromptPreviewResponse(prompt.id, prompt.name)
}
}
🤖 Prompt for AI Agents
In src/main/kotlin/simplerag/ragback/domain/prompt/dto/PromptResponseDTO.kt
around lines 23 to 33, the companion object's from(...) function uses a block
body returning a new instance; change it to an expression body for conciseness
and idiomatic Kotlin by replacing the function block with a single-expression
implementation that directly returns PromptPreviewResponse(prompt.id,
prompt.name).

}

data class PromptDetailResponse(
val id: Long,
val name: String,
val preSet: PreSet,
val systemPrompt: String,
) {
companion object {
fun from(
prompt: Prompt
): PromptDetailResponse {
return PromptDetailResponse(prompt.id, prompt.name, prompt.preSet, prompt.systemPrompt)
}
}
}
53 changes: 36 additions & 17 deletions src/main/kotlin/simplerag/ragback/domain/prompt/entity/Prompt.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,48 +3,67 @@ package simplerag.ragback.domain.prompt.entity
import simplerag.ragback.domain.prompt.entity.enums.PreSet
import jakarta.persistence.*
import simplerag.ragback.domain.prompt.dto.PromptCreateRequest
import simplerag.ragback.domain.prompt.dto.PromptUpdateRequest
import simplerag.ragback.global.entity.BaseEntity
import simplerag.ragback.global.error.ErrorCode
import simplerag.ragback.global.error.PromptException

@Entity
@Table(name = "prompts")
class Prompt(

@Column(name = "name", length = 100, nullable = false)
val name: String,
var name: String,

@Enumerated(EnumType.STRING)
@Column(name = "pre_set", nullable = false)
val preSet: PreSet,
var preSet: PreSet,

@Column(name = "system_prompt", nullable = false)
@Lob
val systemPrompt: String,
var systemPrompt: String,

) : BaseEntity() {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "prompts_id")
val id: Long = 0

companion object {
fun from(
promptCreateRequest: PromptCreateRequest
): Prompt {

val prompt = if (promptCreateRequest.preSet == PreSet.CUSTOM) {
Prompt(
promptCreateRequest.name,
promptCreateRequest.preSet,
promptCreateRequest.systemPrompt
)
} else {
Prompt(
fun from(promptCreateRequest: PromptCreateRequest): Prompt =
when (promptCreateRequest.preSet) {
PreSet.CUSTOM -> Prompt(
promptCreateRequest.name,
promptCreateRequest.preSet,
promptCreateRequest.preSet.defaultSystemPrompt
promptCreateRequest.systemPrompt ?: ""
)
else -> {
if(!promptCreateRequest.systemPrompt.isNullOrBlank()) {
throw PromptException(ErrorCode.CUSTOM_SYSTEM_PROMPT)
}
Prompt(
promptCreateRequest.name,
promptCreateRequest.preSet,
promptCreateRequest.preSet.defaultSystemPrompt
)
}
}
}
Comment on lines +32 to +50
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

DRY the preset rules between create and update.

The same conditional rules appear in both from(...) and update(...). Extract a shared helper to reduce duplication and future drift.

If you want, I can open a follow-up PR to apply this refactor across service/controller tests.

Example helper (outside the shown ranges):

// Inside Prompt (private) or companion
private fun resolveSystemPrompt(preSet: PreSet, candidate: String?): String {
    if (preSet == PreSet.CUSTOM) return candidate?.trim().orEmpty()
    if (!candidate.isNullOrBlank()) throw PromptException(ErrorCode.CUSTOM_SYSTEM_PROMPT)
    return preSet.defaultSystemPrompt
}

Then within the changed sections:

  • Create: systemPrompt = resolveSystemPrompt(promptCreateRequest.preSet, promptCreateRequest.systemPrompt)
  • Update: this.systemPrompt = resolveSystemPrompt(promptUpdateRequest.preSet, promptUpdateRequest.systemPrompt)

Also applies to: 52-66

🤖 Prompt for AI Agents
In src/main/kotlin/simplerag/ragback/domain/prompt/entity/Prompt.kt around lines
32-50 (and also apply to 52-66), the preset-to-systemPrompt resolution logic is
duplicated between from(...) and update(...); extract a single private helper
(e.g., private fun resolveSystemPrompt(preSet: PreSet, candidate: String?):
String) that implements the rules (return trimmed candidate or empty for
PreSet.CUSTOM; throw PromptException when non-blank candidate supplied for
non-CUSTOM; otherwise return preSet.defaultSystemPrompt) and replace the
in-place logic in both from(...) and update(...) to call this helper to set
systemPrompt.

🧹 Nitpick (assertive)

Normalize systemPrompt on create (trim/empty) to avoid storing whitespace noise.

For CUSTOM, you currently persist raw input or empty when null. Trimming avoids accidental leading/trailing whitespace.

                 PreSet.CUSTOM -> Prompt(
                     promptCreateRequest.name,
                     promptCreateRequest.preSet,
-                    promptCreateRequest.systemPrompt ?: ""
+                    promptCreateRequest.systemPrompt?.trim().orEmpty()
                 )
🤖 Prompt for AI Agents
In src/main/kotlin/simplerag/ragback/domain/prompt/entity/Prompt.kt around lines
32 to 50, normalize the incoming systemPrompt by trimming whitespace before
persisting: for PreSet.CUSTOM, use the trimmed value (treat null as empty and if
trimmed is empty persist empty string) instead of the raw input; for non-CUSTOM
keep the existing validation (throw if systemPrompt is not null/blank) but when
constructing the Prompt use the preSet.defaultSystemPrompt unchanged — ensure
all uses call promptCreateRequest.systemPrompt?.trim() to avoid storing
whitespace-only strings.


return prompt
fun update(promptUpdateRequest: PromptUpdateRequest) {
when (promptUpdateRequest.preSet) {
PreSet.CUSTOM -> {
this.name = promptUpdateRequest.name
this.systemPrompt = promptUpdateRequest.systemPrompt ?: ""
this.preSet = promptUpdateRequest.preSet
}
else -> {
if(!promptUpdateRequest.systemPrompt.isNullOrBlank()) {
throw PromptException(ErrorCode.CUSTOM_SYSTEM_PROMPT)
}
this.name = promptUpdateRequest.name
this.systemPrompt = promptUpdateRequest.preSet.defaultSystemPrompt
this.preSet = promptUpdateRequest.preSet
}
Comment on lines +52 to +66
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Normalize inputs on update; enforce same rules with trimmed values.

  • Trim name to prevent accidental whitespace persistence.
  • Trim systemPrompt for CUSTOM before storing.
             PreSet.CUSTOM -> {
-                this.name = promptUpdateRequest.name
-                this.systemPrompt = promptUpdateRequest.systemPrompt ?: ""
+                this.name = promptUpdateRequest.name.trim()
+                this.systemPrompt = promptUpdateRequest.systemPrompt?.trim().orEmpty()
                 this.preSet = promptUpdateRequest.preSet
             }
             else -> {
                 if(!promptUpdateRequest.systemPrompt.isNullOrBlank()) {
                     throw PromptException(ErrorCode.CUSTOM_SYSTEM_PROMPT)
                 }
-                this.name = promptUpdateRequest.name
+                this.name = promptUpdateRequest.name.trim()
                 this.systemPrompt = promptUpdateRequest.preSet.defaultSystemPrompt
                 this.preSet = promptUpdateRequest.preSet
             }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
fun update(promptUpdateRequest: PromptUpdateRequest) {
when (promptUpdateRequest.preSet) {
PreSet.CUSTOM -> {
this.name = promptUpdateRequest.name
this.systemPrompt = promptUpdateRequest.systemPrompt ?: ""
this.preSet = promptUpdateRequest.preSet
}
else -> {
if(!promptUpdateRequest.systemPrompt.isNullOrBlank()) {
throw PromptException(ErrorCode.CUSTOM_SYSTEM_PROMPT)
}
this.name = promptUpdateRequest.name
this.systemPrompt = promptUpdateRequest.preSet.defaultSystemPrompt
this.preSet = promptUpdateRequest.preSet
}
fun update(promptUpdateRequest: PromptUpdateRequest) {
when (promptUpdateRequest.preSet) {
PreSet.CUSTOM -> {
this.name = promptUpdateRequest.name.trim()
this.systemPrompt = promptUpdateRequest.systemPrompt?.trim().orEmpty()
this.preSet = promptUpdateRequest.preSet
}
else -> {
if (!promptUpdateRequest.systemPrompt.isNullOrBlank()) {
throw PromptException(ErrorCode.CUSTOM_SYSTEM_PROMPT)
}
this.name = promptUpdateRequest.name.trim()
this.systemPrompt = promptUpdateRequest.preSet.defaultSystemPrompt
this.preSet = promptUpdateRequest.preSet
}
}
}
🤖 Prompt for AI Agents
In src/main/kotlin/simplerag/ragback/domain/prompt/entity/Prompt.kt around lines
52 to 66, inputs are not normalized on update: trim the incoming
promptUpdateRequest.name and promptUpdateRequest.systemPrompt before validation
and assignment. Specifically, for PreSet.CUSTOM trim the name and trim
systemPrompt (use empty string if null after trim) before storing; for
non-CUSTOM, perform the isNullOrBlank check on the trimmed systemPrompt and
assign name and systemPrompt using trimmed values (use
preSet.defaultSystemPrompt for systemPrompt when appropriate) and set preSet as
before.

}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package simplerag.ragback.domain.prompt.repository

import org.springframework.data.domain.Pageable
import org.springframework.data.domain.Slice
import org.springframework.data.jpa.repository.JpaRepository
import simplerag.ragback.domain.prompt.entity.Prompt

interface PromptRepository: JpaRepository<Prompt, Long>
interface PromptRepository: JpaRepository<Prompt, Long> {
fun findByIdGreaterThanOrderById(cursor: Long, pageable: Pageable): Slice<Prompt>
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
package simplerag.ragback.domain.prompt.service

import org.springframework.data.domain.PageRequest
import org.springframework.data.repository.findByIdOrNull
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import simplerag.ragback.domain.prompt.dto.PromptCreateRequest
import simplerag.ragback.domain.prompt.dto.PromptPreviewResponse
import simplerag.ragback.domain.document.dto.DataFileDetailResponseList
import simplerag.ragback.domain.document.dto.TagDTO
import simplerag.ragback.domain.prompt.dto.*
import simplerag.ragback.domain.prompt.entity.Prompt
import simplerag.ragback.domain.prompt.repository.PromptRepository
import simplerag.ragback.global.error.ErrorCode
import simplerag.ragback.global.error.PromptException

@Service
@Transactional(readOnly = true)
Expand All @@ -21,4 +26,36 @@ class PromptService(
val savedPrompt = promptRepository.save(prompt)
return PromptPreviewResponse.from(savedPrompt)
}

fun getPrompt(promptId: Long): PromptDetailResponse {
val prompt = promptRepository.findByIdOrNull(promptId) ?: throw PromptException(ErrorCode.NOT_FOUND)
return PromptDetailResponse.from(prompt)
}

fun getPrompts(
cursor: Long,
take: Int
): PromptPreviewResponseList {
val prompts = promptRepository.findByIdGreaterThanOrderById(cursor, PageRequest.of(0, take))

val nextCursor = prompts.content.lastOrNull()?.id

return PromptPreviewResponseList.from(prompts.content, nextCursor, prompts.hasNext())
}
Comment on lines +35 to +44
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Harden paging inputs: clamp take and prevent negative cursors.

Avoid accidental large page sizes and negative cursors that could stress DB or yield confusing results. Also aligns with cursor semantics used in the DTO (nextCursor).

     fun getPrompts(
         cursor: Long,
         take: Int
     ): PromptPreviewResponseList {
-        val prompts = promptRepository.findByIdGreaterThanOrderById(cursor, PageRequest.of(0, take))
+        val safeTake = take.coerceIn(1, 100)
+        val safeCursor = cursor.coerceAtLeast(0L)
+        val prompts = promptRepository.findByIdGreaterThanOrderById(
+            safeCursor,
+            PageRequest.of(0, safeTake)
+        )
 
         val nextCursor = prompts.content.lastOrNull()?.id
 
         return PromptPreviewResponseList.from(prompts.content, nextCursor, prompts.hasNext())
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
fun getPrompts(
cursor: Long,
take: Int
): PromptPreviewResponseList {
val prompts = promptRepository.findByIdGreaterThanOrderById(cursor, PageRequest.of(0, take))
val nextCursor = prompts.content.lastOrNull()?.id
return PromptPreviewResponseList.from(prompts.content, nextCursor, prompts.hasNext())
}
fun getPrompts(
cursor: Long,
take: Int
): PromptPreviewResponseList {
val safeTake = take.coerceIn(1, 100)
val safeCursor = cursor.coerceAtLeast(0L)
val prompts = promptRepository.findByIdGreaterThanOrderById(
safeCursor,
PageRequest.of(0, safeTake)
)
val nextCursor = prompts.content.lastOrNull()?.id
return PromptPreviewResponseList.from(prompts.content, nextCursor, prompts.hasNext())
}
🤖 Prompt for AI Agents
In src/main/kotlin/simplerag/ragback/domain/prompt/service/PromptService.kt
around lines 35 to 44, the paging inputs are not validated: clamp the requested
take to a safe min/max (e.g. at least 1 and a reasonable max like 100) before
creating the PageRequest, and ensure cursor is non-negative (treat negative
cursor as 0 or throw IllegalArgumentException). Modify the method to sanitize
cursor and take up-front, then call
promptRepository.findByIdGreaterThanOrderById with PageRequest.of(0,
sanitizedTake) and compute nextCursor as before.


@Transactional
fun updatePrompt(
promptUpdateRequest: PromptUpdateRequest,
promptId: Long
): PromptPreviewResponse {
val prompt = promptRepository.findByIdOrNull(promptId) ?: throw PromptException(ErrorCode.NOT_FOUND)
prompt.update(promptUpdateRequest)
return PromptPreviewResponse.from(prompt)
}
Comment on lines +46 to +54
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Reduce repetition: extract a find-or-throw helper.

You repeat the NOT_FOUND lookup pattern. Centralize it for clarity and consistency.

-        val prompt = promptRepository.findByIdOrNull(promptId) ?: throw  PromptException(ErrorCode.NOT_FOUND)
+        val prompt = findPromptOrThrow(promptId)
         prompt.update(promptUpdateRequest)
         return PromptPreviewResponse.from(prompt)

Add this helper (outside the shown ranges):

private fun findPromptOrThrow(id: Long): Prompt =
    promptRepository.findByIdOrNull(id) ?: throw PromptException(ErrorCode.NOT_FOUND)
🤖 Prompt for AI Agents
In src/main/kotlin/simplerag/ragback/domain/prompt/service/PromptService.kt
around lines 46 to 54, you're repeating the "find by id or throw NOT_FOUND"
pattern; add a private helper method named findPromptOrThrow that looks up a
Prompt by id via promptRepository.findByIdOrNull and throws
PromptException(ErrorCode.NOT_FOUND) when null, then replace the inline lookup
in updatePrompt (and any other occurrences) to call this helper so the lookup
and exception logic is centralized and consistent.


@Transactional
fun deletePrompt(promptId: Long) {
val prompt = promptRepository.findByIdOrNull(promptId) ?: throw PromptException(ErrorCode.NOT_FOUND)
promptRepository.delete(prompt)
}
Comment on lines +56 to +60
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Decide on DELETE semantics: strict 404 vs idempotent delete.

Current behavior throws NOT_FOUND if the prompt doesn't exist. Many APIs choose idempotent deletes (204 even if absent) to reduce information leakage and simplify clients. If you prefer idempotency, consider no-op on missing id.

🤖 Prompt for AI Agents
In src/main/kotlin/simplerag/ragback/domain/prompt/service/PromptService.kt
around lines 56 to 60, the deletePrompt currently throws a NOT_FOUND
PromptException when the prompt is missing; decide on DELETE semantics and
implement idempotent delete: change the method to be a no-op when the prompt
doesn't exist (do not throw), e.g., check repository existence and return
silently or use deleteById without raising if absent, ensuring callers receive
success for repeated deletes; if you prefer strict behavior instead, keep the
current exception but update docs/tests to assert 404 on missing resources.

}
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,9 @@ class FileException(
override val errorCode: ErrorCode,
override val message: String,
override val cause: Throwable? = null,
) : CustomException(errorCode, message, cause)
) : CustomException(errorCode, message, cause)

class PromptException(
override val errorCode: ErrorCode,
override val cause: Throwable? = null,
) : CustomException(errorCode, errorCode.message, cause)
5 changes: 4 additions & 1 deletion src/main/kotlin/simplerag/ragback/global/error/ErrorCode.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,8 @@ enum class ErrorCode(
S3_UNSUPPORTED_CONTENT_TYPE(HttpStatus.BAD_REQUEST, "S3_007", "지원하지 않는 Content-Type 입니다."),

// index
OVERLAP_OVERFLOW(HttpStatus.BAD_REQUEST, "INDEX_001", "overlap 크기는 chunking 크기를 넘을 수 없습니다.")
OVERLAP_OVERFLOW(HttpStatus.BAD_REQUEST, "INDEX_001", "overlap 크기는 chunking 크기를 넘을 수 없습니다."),

// prompt
CUSTOM_SYSTEM_PROMPT(HttpStatus.BAD_REQUEST, "PROMPT_001", "preset이 Custom이 아닐 때 systemPrompt의 조작이 불가능합니다.")
Comment on lines +28 to +31
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Nit: Keep preset name casing consistent with enum value

Use “CUSTOM” (enum name) instead of “Custom” in the message for consistency and easier grep/search.

Apply this diff:

-    CUSTOM_SYSTEM_PROMPT(HttpStatus.BAD_REQUEST, "PROMPT_001", "preset이 Custom이 아닐 때 systemPrompt의 조작이 불가능합니다.")
+    CUSTOM_SYSTEM_PROMPT(HttpStatus.BAD_REQUEST, "PROMPT_001", "preset이 CUSTOM이 아닐 때 systemPrompt의 조작이 불가능합니다.")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
OVERLAP_OVERFLOW(HttpStatus.BAD_REQUEST, "INDEX_001", "overlap 크기는 chunking 크기를 넘을 수 없습니다."),
// prompt
CUSTOM_SYSTEM_PROMPT(HttpStatus.BAD_REQUEST, "PROMPT_001", "preset이 Custom이 아닐 때 systemPrompt의 조작이 불가능합니다.")
OVERLAP_OVERFLOW(HttpStatus.BAD_REQUEST, "INDEX_001", "overlap 크기는 chunking 크기를 넘을 수 없습니다."),
// prompt
CUSTOM_SYSTEM_PROMPT(HttpStatus.BAD_REQUEST, "PROMPT_001", "preset이 CUSTOM이 아닐 때 systemPrompt의 조작이 불가능합니다.")
🤖 Prompt for AI Agents
In src/main/kotlin/simplerag/ragback/global/error/ErrorCode.kt around lines 28
to 31, the error message for CUSTOM_SYSTEM_PROMPT uses "Custom" while the enum
name is CUSTOM; update the message string to use "CUSTOM" (uppercase) so casing
matches the enum and is easier to grep, leaving the rest of the enum entry
unchanged.

}