From c93186cc52da8b5c4ae88e17c9be1705a0f8b807 Mon Sep 17 00:00:00 2001 From: Jari Date: Sat, 18 Oct 2025 16:00:58 +0200 Subject: [PATCH 1/7] Add creation --- .gitignore | 3 +- .../octopus/internal/services/EntryService.kt | 2 +- .../services/impl/EntryServiceImpl.kt | 31 +++++++++++++++++-- .../web/controllers/EntryController.kt | 10 ++++++ .../octopus/internal/web/utils/BaseAdvice.kt | 3 +- .../internal/web/utils/dtos/EntryDto.kt | 2 ++ 6 files changed, 45 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index dcb6c75..6e9e444 100644 --- a/.gitignore +++ b/.gitignore @@ -46,4 +46,5 @@ bin/ .idea/* .env -data/* \ No newline at end of file +data/* +testSuite/* \ No newline at end of file diff --git a/src/main/kotlin/org/octopus/internal/services/EntryService.kt b/src/main/kotlin/org/octopus/internal/services/EntryService.kt index f6416fc..e2e0043 100755 --- a/src/main/kotlin/org/octopus/internal/services/EntryService.kt +++ b/src/main/kotlin/org/octopus/internal/services/EntryService.kt @@ -4,6 +4,6 @@ import org.octopus.internal.common.models.Entry import org.octopus.internal.web.utils.dtos.EntryDto interface EntryService { - fun createEntry(entry: EntryDto) + fun createEntry(entry: EntryDto): Entry fun getAllEntries(): MutableList } \ No newline at end of file diff --git a/src/main/kotlin/org/octopus/internal/services/impl/EntryServiceImpl.kt b/src/main/kotlin/org/octopus/internal/services/impl/EntryServiceImpl.kt index 6511374..ef168a9 100755 --- a/src/main/kotlin/org/octopus/internal/services/impl/EntryServiceImpl.kt +++ b/src/main/kotlin/org/octopus/internal/services/impl/EntryServiceImpl.kt @@ -1,20 +1,47 @@ package org.octopus.internal.services.impl +import org.octopus.internal.common.enums.EBusinessException import org.octopus.internal.common.enums.EEntryType +import org.octopus.internal.common.exceptions.OctopusPlanningException import org.octopus.internal.common.models.Entry import org.octopus.internal.repositories.EntryRepository import org.octopus.internal.services.EntryService import org.octopus.internal.web.utils.dtos.EntryDto import org.springframework.stereotype.Service +import java.time.Month @Service class EntryServiceImpl(val repo: EntryRepository) : EntryService { - override fun createEntry(entry: EntryDto) { - TODO("Not yet implemented") + override fun createEntry(entry: EntryDto): Entry { + if (!verifyDto(entry)) { + throw OctopusPlanningException.create(EBusinessException.INVALID_REQUEST, entry, "Entry must be either recurring or fixed.") + } + + return repo.createEntry(Entry( + id = 0L, + type = EEntryType.valueOf(entry.type), + amount = entry.amount, + fixedDate = entry.fixedDate, + endDate = entry.endDate, + recurrent = entry.recurrent, + recurrentMonths = entry.recurrentMonths?.map { m -> Month.of(m) } + )) } override fun getAllEntries(): MutableList { TODO("Not yet implemented") } + private fun verifyDto(entry: EntryDto): Boolean { + val eventIsRecurring = entry.fixedDate == null && + entry.endDate != null && + entry.recurrent == true && + entry.recurrentMonths?.isNotEmpty() ?: false + val eventIsFixed = entry.fixedDate != null && + entry.endDate == null && + entry.recurrent != true && + entry.recurrentMonths?.isEmpty() ?: true + return eventIsRecurring || eventIsFixed + } + } \ No newline at end of file diff --git a/src/main/kotlin/org/octopus/internal/web/controllers/EntryController.kt b/src/main/kotlin/org/octopus/internal/web/controllers/EntryController.kt index 5d36a6e..c853c3d 100755 --- a/src/main/kotlin/org/octopus/internal/web/controllers/EntryController.kt +++ b/src/main/kotlin/org/octopus/internal/web/controllers/EntryController.kt @@ -1,9 +1,12 @@ package org.octopus.internal.web.controllers +import jakarta.validation.Valid import lombok.AllArgsConstructor import org.octopus.internal.common.models.Entry import org.octopus.internal.services.EntryService +import org.octopus.internal.web.utils.dtos.EntryDto import org.octopus.internal.web.utils.responses.WebResponse +import org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties.Web import org.springframework.web.bind.annotation.* @RestController @@ -13,6 +16,13 @@ class EntryController( val entryService: EntryService ) { + @PostMapping + fun createEntry( + @Valid @RequestBody entryDto: EntryDto + ): WebResponse { + return WebResponse.ok(entryService.createEntry(entryDto)) + } + @GetMapping fun getAllClients(): WebResponse> { return WebResponse.ok(entryService.getAllEntries()) diff --git a/src/main/kotlin/org/octopus/internal/web/utils/BaseAdvice.kt b/src/main/kotlin/org/octopus/internal/web/utils/BaseAdvice.kt index 19d6228..74a73ab 100755 --- a/src/main/kotlin/org/octopus/internal/web/utils/BaseAdvice.kt +++ b/src/main/kotlin/org/octopus/internal/web/utils/BaseAdvice.kt @@ -95,8 +95,7 @@ class BaseAdvice { ): WebResponse { return WebResponse.ko( HttpStatus.NOT_ACCEPTABLE, - "${HttpStatus.NOT_ACCEPTABLE.reasonPhrase}: JSON parse error" - + "${HttpStatus.NOT_ACCEPTABLE.reasonPhrase}: JSON parse error. Please verify the body has the correct format.", ) } diff --git a/src/main/kotlin/org/octopus/internal/web/utils/dtos/EntryDto.kt b/src/main/kotlin/org/octopus/internal/web/utils/dtos/EntryDto.kt index 198b3ed..13cd292 100755 --- a/src/main/kotlin/org/octopus/internal/web/utils/dtos/EntryDto.kt +++ b/src/main/kotlin/org/octopus/internal/web/utils/dtos/EntryDto.kt @@ -1,5 +1,6 @@ package org.octopus.internal.web.utils.dtos +import jakarta.validation.constraints.DecimalMin import org.octopus.internal.web.utils.dtos.validators.EntryMonthValidator import org.octopus.internal.web.utils.dtos.validators.EntryTypeValidator import java.util.Date @@ -7,6 +8,7 @@ import java.util.Date data class EntryDto( @field:EntryTypeValidator.Validate val type: String, + @field:DecimalMin(value="0.00", inclusive = false, message = "Amount value cannot be lower than 0.01") val amount: Double, val fixedDate: Date?, val recurrent: Boolean?, -- 2.40.1 From 8e3f94527d530a8a594bcd1d87b9be7bb9236eb1 Mon Sep 17 00:00:00 2001 From: Jari Date: Sat, 18 Oct 2025 17:17:47 +0200 Subject: [PATCH 2/7] More operations --- .../octopus/internal/common/models/Entry.kt | 1 + .../internal/db/entities/EntryEntity.kt | 4 +++ .../internal/repositories/EntryRepository.kt | 3 +++ .../repositories/impl/EntryRepositoryImpl.kt | 11 ++++++++ .../octopus/internal/services/EntryService.kt | 3 +++ .../services/impl/EntryServiceImpl.kt | 25 +++++++++++++++++-- .../web/controllers/EntryController.kt | 11 +++++++- .../internal/web/utils/dtos/EntryDto.kt | 9 ++++--- 8 files changed, 61 insertions(+), 6 deletions(-) diff --git a/src/main/kotlin/org/octopus/internal/common/models/Entry.kt b/src/main/kotlin/org/octopus/internal/common/models/Entry.kt index 70621c4..7162f2b 100755 --- a/src/main/kotlin/org/octopus/internal/common/models/Entry.kt +++ b/src/main/kotlin/org/octopus/internal/common/models/Entry.kt @@ -11,5 +11,6 @@ data class Entry( val fixedDate: Date? = null, val recurrent: Boolean? = false, val recurrentMonths: List? = mutableListOf(), + val startDate: Date? = null, val endDate: Date? = null ) \ No newline at end of file diff --git a/src/main/kotlin/org/octopus/internal/db/entities/EntryEntity.kt b/src/main/kotlin/org/octopus/internal/db/entities/EntryEntity.kt index d6487eb..d5b9436 100644 --- a/src/main/kotlin/org/octopus/internal/db/entities/EntryEntity.kt +++ b/src/main/kotlin/org/octopus/internal/db/entities/EntryEntity.kt @@ -1,10 +1,12 @@ package org.octopus.internal.db.entities +import jakarta.persistence.Convert import jakarta.persistence.Entity import jakarta.persistence.GeneratedValue import jakarta.persistence.GenerationType import jakarta.persistence.Id import org.octopus.internal.common.enums.EEntryType +import org.octopus.internal.db.converters.MonthListConverter import java.time.Month import java.util.Date @@ -16,6 +18,8 @@ data class EntryEntity( val amount: Double, val fixedDate: Date? = null, val recurrent: Boolean? = false, + @field:Convert(converter = MonthListConverter::class) val recurrentMonths: List? = mutableListOf(), + val startDate: Date? = null, val endDate: Date? = null ) \ No newline at end of file diff --git a/src/main/kotlin/org/octopus/internal/repositories/EntryRepository.kt b/src/main/kotlin/org/octopus/internal/repositories/EntryRepository.kt index 3a3cdd5..257c97a 100755 --- a/src/main/kotlin/org/octopus/internal/repositories/EntryRepository.kt +++ b/src/main/kotlin/org/octopus/internal/repositories/EntryRepository.kt @@ -13,4 +13,7 @@ interface EntryRepository { fun getAllRecurrentOnAllMonths(): MutableList fun getAllFixedUpToEndDate(endDate: Date): MutableList fun getAllByType(type: EEntryType): MutableList + + fun deleteById(id: Long): Boolean + fun deleteAll(): Boolean } diff --git a/src/main/kotlin/org/octopus/internal/repositories/impl/EntryRepositoryImpl.kt b/src/main/kotlin/org/octopus/internal/repositories/impl/EntryRepositoryImpl.kt index 3121ade..dc0aed4 100755 --- a/src/main/kotlin/org/octopus/internal/repositories/impl/EntryRepositoryImpl.kt +++ b/src/main/kotlin/org/octopus/internal/repositories/impl/EntryRepositoryImpl.kt @@ -37,6 +37,17 @@ class EntryRepositoryImpl( return mapper.toModels(jpa.findAllByType(type)) } + override fun deleteById(id: Long): Boolean { + jpa.deleteById(id) + return true + + } + + override fun deleteAll(): Boolean { + jpa.deleteAll() + return true + } + override fun getAllRecurrentMonthlyToEndDate(endDate: Date): MutableList { return mapper.toModels( jpa.findAllByFixedDateIsNullAndRecurrentIsTrueAndEndDateIsNotNullAndEndDateBefore(endDate) diff --git a/src/main/kotlin/org/octopus/internal/services/EntryService.kt b/src/main/kotlin/org/octopus/internal/services/EntryService.kt index e2e0043..4042900 100755 --- a/src/main/kotlin/org/octopus/internal/services/EntryService.kt +++ b/src/main/kotlin/org/octopus/internal/services/EntryService.kt @@ -6,4 +6,7 @@ import org.octopus.internal.web.utils.dtos.EntryDto interface EntryService { fun createEntry(entry: EntryDto): Entry fun getAllEntries(): MutableList + fun deleteAll(): Boolean + fun deleteById(id: Long): Boolean + fun editEntry(id: Long, entry: EntryDto): Entry } \ No newline at end of file diff --git a/src/main/kotlin/org/octopus/internal/services/impl/EntryServiceImpl.kt b/src/main/kotlin/org/octopus/internal/services/impl/EntryServiceImpl.kt index ef168a9..226e6e8 100755 --- a/src/main/kotlin/org/octopus/internal/services/impl/EntryServiceImpl.kt +++ b/src/main/kotlin/org/octopus/internal/services/impl/EntryServiceImpl.kt @@ -22,22 +22,43 @@ class EntryServiceImpl(val repo: EntryRepository) : EntryService { type = EEntryType.valueOf(entry.type), amount = entry.amount, fixedDate = entry.fixedDate, + startDate = entry.startDate, endDate = entry.endDate, recurrent = entry.recurrent, - recurrentMonths = entry.recurrentMonths?.map { m -> Month.of(m) } + recurrentMonths = entry.recurrentMonths?.map { m -> Month.of(m) }?.toList() ?: emptyList() )) } override fun getAllEntries(): MutableList { - TODO("Not yet implemented") + val income = repo.getAllByType(EEntryType.INCOME) + val outcome = repo.getAllByType(EEntryType.OUTCOME) + val invest = repo.getAllByType(EEntryType.INVESTMENT) + return (income + outcome + invest).toMutableList() + } + + override fun deleteAll(): Boolean { + return repo.deleteAll() + } + + override fun deleteById(id: Long): Boolean { + return repo.deleteById(id) + } + + override fun editEntry(id: Long, entry: EntryDto): Entry { +// var savedEntry = repo.getById(id) +// savedEntry.startDate = entry.startDate + TODO("FINIRE STA MERDA") + return repo.getById(id) } private fun verifyDto(entry: EntryDto): Boolean { val eventIsRecurring = entry.fixedDate == null && + entry.startDate != null && entry.endDate != null && entry.recurrent == true && entry.recurrentMonths?.isNotEmpty() ?: false val eventIsFixed = entry.fixedDate != null && + entry.startDate == null && entry.endDate == null && entry.recurrent != true && entry.recurrentMonths?.isEmpty() ?: true diff --git a/src/main/kotlin/org/octopus/internal/web/controllers/EntryController.kt b/src/main/kotlin/org/octopus/internal/web/controllers/EntryController.kt index c853c3d..109719b 100755 --- a/src/main/kotlin/org/octopus/internal/web/controllers/EntryController.kt +++ b/src/main/kotlin/org/octopus/internal/web/controllers/EntryController.kt @@ -24,10 +24,19 @@ class EntryController( } @GetMapping - fun getAllClients(): WebResponse> { + fun getAllEntries(): WebResponse> { return WebResponse.ok(entryService.getAllEntries()) } + @DeleteMapping + fun deleteAllEntries(): WebResponse { + return WebResponse.ok(entryService.deleteAll()) + } + + @DeleteMapping("/{id}") + fun deleteEntryById(@PathVariable("id") id: Long): WebResponse { + return WebResponse.ok(entryService.deleteById(id)) + } // @GetMapping("/{id}") // fun getClient(@PathVariable("id") id: String, @RequestParam( diff --git a/src/main/kotlin/org/octopus/internal/web/utils/dtos/EntryDto.kt b/src/main/kotlin/org/octopus/internal/web/utils/dtos/EntryDto.kt index 13cd292..117fca2 100755 --- a/src/main/kotlin/org/octopus/internal/web/utils/dtos/EntryDto.kt +++ b/src/main/kotlin/org/octopus/internal/web/utils/dtos/EntryDto.kt @@ -1,19 +1,22 @@ package org.octopus.internal.web.utils.dtos import jakarta.validation.constraints.DecimalMin +import org.hibernate.validator.constraints.Length import org.octopus.internal.web.utils.dtos.validators.EntryMonthValidator import org.octopus.internal.web.utils.dtos.validators.EntryTypeValidator import java.util.Date data class EntryDto( + @field:Length(min = 1, max = 50, message = "The name must be between 1 and 50 characters long.") @field:EntryTypeValidator.Validate val type: String, - @field:DecimalMin(value="0.00", inclusive = false, message = "Amount value cannot be lower than 0.01") + @field:DecimalMin(value="0.00", inclusive = false, message = "The amount must be greater than 0.00.") val amount: Double, val fixedDate: Date?, val recurrent: Boolean?, @field:EntryMonthValidator.Validate val recurrentMonths: List?, - val endDate: Date? - + val startDate: Date?, + val endDate: Date?, + val color: String = "0xFFFFFF" ) \ No newline at end of file -- 2.40.1 From 6ee21f0e879ea222754915c9bed26aec33515295 Mon Sep 17 00:00:00 2001 From: Jari Date: Sat, 18 Oct 2025 17:18:13 +0200 Subject: [PATCH 3/7] Add Converter --- .../db/converters/MonthListConverter.kt | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 src/main/kotlin/org/octopus/internal/db/converters/MonthListConverter.kt diff --git a/src/main/kotlin/org/octopus/internal/db/converters/MonthListConverter.kt b/src/main/kotlin/org/octopus/internal/db/converters/MonthListConverter.kt new file mode 100644 index 0000000..e27f8be --- /dev/null +++ b/src/main/kotlin/org/octopus/internal/db/converters/MonthListConverter.kt @@ -0,0 +1,19 @@ +package org.octopus.internal.db.converters + +import jakarta.persistence.AttributeConverter +import jakarta.persistence.Converter +import java.time.Month + +@Converter +class MonthListConverter : AttributeConverter, String> { + + override fun convertToDatabaseColumn(attribute: List?): String? { + return attribute?.joinToString(separator = ",") { it.name } + } + + override fun convertToEntityAttribute(dbData: String?): List? { + return dbData?.split(",") + ?.filter { it.isNotBlank() } + ?.mapNotNull { runCatching { Month.valueOf(it.trim()) }.getOrNull() } + } +} \ No newline at end of file -- 2.40.1 From 80fb3db54711d0b4e5e068d6ba1dd1b8e9850e4e Mon Sep 17 00:00:00 2001 From: Jari Date: Sun, 19 Oct 2025 15:18:42 +0200 Subject: [PATCH 4/7] Refine logic --- .../octopus/internal/common/models/Entry.kt | 15 ++++++++++-- .../internal/db/entities/EntryEntity.kt | 4 +++- .../services/impl/EntryServiceImpl.kt | 24 +++++++++++++++---- .../web/controllers/EntryController.kt | 6 +++++ .../internal/web/utils/dtos/EntryDto.kt | 3 +++ 5 files changed, 44 insertions(+), 8 deletions(-) diff --git a/src/main/kotlin/org/octopus/internal/common/models/Entry.kt b/src/main/kotlin/org/octopus/internal/common/models/Entry.kt index 7162f2b..f9ed569 100755 --- a/src/main/kotlin/org/octopus/internal/common/models/Entry.kt +++ b/src/main/kotlin/org/octopus/internal/common/models/Entry.kt @@ -1,16 +1,27 @@ package org.octopus.internal.common.models +import com.fasterxml.jackson.annotation.JsonGetter +import com.fasterxml.jackson.annotation.JsonIgnore import org.octopus.internal.common.enums.EEntryType import java.time.Month import java.util.* data class Entry( var id: Long?, + val name: String, val type: EEntryType, val amount: Double, val fixedDate: Date? = null, val recurrent: Boolean? = false, + @get:JsonIgnore val recurrentMonths: List? = mutableListOf(), val startDate: Date? = null, - val endDate: Date? = null -) \ No newline at end of file + val endDate: Date? = null, + val color: String +){ + + @JsonGetter("recurrentMonths") + fun getRecurrentMonthsAsInts(): List? { + return this.recurrentMonths?.map { it.value } + } +} \ No newline at end of file diff --git a/src/main/kotlin/org/octopus/internal/db/entities/EntryEntity.kt b/src/main/kotlin/org/octopus/internal/db/entities/EntryEntity.kt index d5b9436..63be77b 100644 --- a/src/main/kotlin/org/octopus/internal/db/entities/EntryEntity.kt +++ b/src/main/kotlin/org/octopus/internal/db/entities/EntryEntity.kt @@ -14,6 +14,7 @@ import java.util.Date data class EntryEntity( @Id @GeneratedValue(strategy = GenerationType.IDENTITY) var id: Long = 0L, + val name: String, val type: EEntryType, val amount: Double, val fixedDate: Date? = null, @@ -21,5 +22,6 @@ data class EntryEntity( @field:Convert(converter = MonthListConverter::class) val recurrentMonths: List? = mutableListOf(), val startDate: Date? = null, - val endDate: Date? = null + val endDate: Date? = null, + val color: String ) \ No newline at end of file diff --git a/src/main/kotlin/org/octopus/internal/services/impl/EntryServiceImpl.kt b/src/main/kotlin/org/octopus/internal/services/impl/EntryServiceImpl.kt index 226e6e8..1535903 100755 --- a/src/main/kotlin/org/octopus/internal/services/impl/EntryServiceImpl.kt +++ b/src/main/kotlin/org/octopus/internal/services/impl/EntryServiceImpl.kt @@ -19,13 +19,15 @@ class EntryServiceImpl(val repo: EntryRepository) : EntryService { return repo.createEntry(Entry( id = 0L, + name = entry.name, type = EEntryType.valueOf(entry.type), amount = entry.amount, fixedDate = entry.fixedDate, startDate = entry.startDate, endDate = entry.endDate, recurrent = entry.recurrent, - recurrentMonths = entry.recurrentMonths?.map { m -> Month.of(m) }?.toList() ?: emptyList() + recurrentMonths = entry.recurrentMonths?.map { m -> Month.of(m) }?.toList() ?: emptyList(), + color = entry.color )) } @@ -45,10 +47,22 @@ class EntryServiceImpl(val repo: EntryRepository) : EntryService { } override fun editEntry(id: Long, entry: EntryDto): Entry { -// var savedEntry = repo.getById(id) -// savedEntry.startDate = entry.startDate - TODO("FINIRE STA MERDA") - return repo.getById(id) + if (!verifyDto(entry)) { + throw OctopusPlanningException.create(EBusinessException.INVALID_REQUEST, entry, "Entry must be either recurring or fixed.") + } + + return repo.createEntry(Entry( + id = id, + name = entry.name, + type = EEntryType.valueOf(entry.type), + amount = entry.amount, + fixedDate = entry.fixedDate, + startDate = entry.startDate, + endDate = entry.endDate, + recurrent = entry.recurrent, + recurrentMonths = entry.recurrentMonths?.map { m -> Month.of(m) }?.toList() ?: emptyList(), + color = entry.color + )) } private fun verifyDto(entry: EntryDto): Boolean { diff --git a/src/main/kotlin/org/octopus/internal/web/controllers/EntryController.kt b/src/main/kotlin/org/octopus/internal/web/controllers/EntryController.kt index 109719b..1ddb67c 100755 --- a/src/main/kotlin/org/octopus/internal/web/controllers/EntryController.kt +++ b/src/main/kotlin/org/octopus/internal/web/controllers/EntryController.kt @@ -38,6 +38,12 @@ class EntryController( return WebResponse.ok(entryService.deleteById(id)) } + @PutMapping("/{id}") + fun editEntryById(@PathVariable("id") id: Long, + @Valid @RequestBody entryDto: EntryDto): WebResponse { + return WebResponse.ok(entryService.editEntry(id, entryDto)) + } + // @GetMapping("/{id}") // fun getClient(@PathVariable("id") id: String, @RequestParam( // "includeDeactivated", diff --git a/src/main/kotlin/org/octopus/internal/web/utils/dtos/EntryDto.kt b/src/main/kotlin/org/octopus/internal/web/utils/dtos/EntryDto.kt index 117fca2..290c8ab 100755 --- a/src/main/kotlin/org/octopus/internal/web/utils/dtos/EntryDto.kt +++ b/src/main/kotlin/org/octopus/internal/web/utils/dtos/EntryDto.kt @@ -2,12 +2,14 @@ package org.octopus.internal.web.utils.dtos import jakarta.validation.constraints.DecimalMin import org.hibernate.validator.constraints.Length +import org.octopus.internal.web.utils.dtos.validators.EntryHexColorValidator import org.octopus.internal.web.utils.dtos.validators.EntryMonthValidator import org.octopus.internal.web.utils.dtos.validators.EntryTypeValidator import java.util.Date data class EntryDto( @field:Length(min = 1, max = 50, message = "The name must be between 1 and 50 characters long.") + val name: String, @field:EntryTypeValidator.Validate val type: String, @field:DecimalMin(value="0.00", inclusive = false, message = "The amount must be greater than 0.00.") @@ -18,5 +20,6 @@ data class EntryDto( val recurrentMonths: List?, val startDate: Date?, val endDate: Date?, + @field:EntryHexColorValidator.Validate val color: String = "0xFFFFFF" ) \ No newline at end of file -- 2.40.1 From 819c74bb57899737c27e1f27bbe925777dbf896b Mon Sep 17 00:00:00 2001 From: Jari Date: Sun, 19 Oct 2025 15:19:14 +0200 Subject: [PATCH 5/7] =?UTF-8?q?git=20porcodio=20per=C3=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dtos/validators/EntryHexColorValidator.kt | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100755 src/main/kotlin/org/octopus/internal/web/utils/dtos/validators/EntryHexColorValidator.kt diff --git a/src/main/kotlin/org/octopus/internal/web/utils/dtos/validators/EntryHexColorValidator.kt b/src/main/kotlin/org/octopus/internal/web/utils/dtos/validators/EntryHexColorValidator.kt new file mode 100755 index 0000000..ecca425 --- /dev/null +++ b/src/main/kotlin/org/octopus/internal/web/utils/dtos/validators/EntryHexColorValidator.kt @@ -0,0 +1,41 @@ +package org.octopus.internal.web.utils.dtos.validators + +import jakarta.validation.Constraint +import jakarta.validation.ConstraintValidator +import jakarta.validation.ConstraintValidatorContext +import jakarta.validation.Payload +import org.octopus.internal.common.enums.EEntryType +import kotlin.reflect.KClass + + +class EntryHexColorValidator : ConstraintValidator { + companion object { + private val HEX_COLOR_PATTERN = Regex("^0x([0-9a-fA-F]{6}|[0-9a-fA-F]{3})$") + } + + override fun isValid(value: String?, context: ConstraintValidatorContext): Boolean { + if (value.isNullOrBlank()) { + return false + } + + val isMatched = HEX_COLOR_PATTERN.matches(value) + + if (!isMatched) { + context.disableDefaultConstraintViolation() + context.buildConstraintViolationWithTemplate("Invalid hex color format. Value must be in 0xRRGGBB format.") + .addConstraintViolation() + } + + return isMatched + } + + @Target(AnnotationTarget.FIELD, AnnotationTarget.PROPERTY, AnnotationTarget.VALUE_PARAMETER) + @Retention(AnnotationRetention.RUNTIME) + @MustBeDocumented + @Constraint(validatedBy = [EntryHexColorValidator::class]) + annotation class Validate( + val message: String = "", + val groups: Array> = [], + val payload: Array> = [] + ) +} \ No newline at end of file -- 2.40.1 From f7f3b1c80bad7c4b0ca869ac58cce770dbf3bebb Mon Sep 17 00:00:00 2001 From: Jari Date: Sun, 19 Oct 2025 15:27:12 +0200 Subject: [PATCH 6/7] =?UTF-8?q?swaggeri=C3=B1o?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle.kts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/build.gradle.kts b/build.gradle.kts index e7ad953..633ec19 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -51,6 +51,9 @@ dependencies { implementation("org.jetbrains.kotlin:kotlin-reflect") kapt("org.springframework.boot:spring-boot-configuration-processor") + implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.0") + implementation("com.fasterxml.jackson.module:jackson-module-kotlin") + // implementation("io.jsonwebtoken:jjwt-api:0.12.6") // runtimeOnly("io.jsonwebtoken:jjwt-impl:0.12.6") // runtimeOnly("io.jsonwebtoken:jjwt-gson:0.12.6") -- 2.40.1 From e9ccb15b4e3d1a961e605a819f1b3c43cc367b57 Mon Sep 17 00:00:00 2001 From: Jari Date: Sun, 19 Oct 2025 15:39:19 +0200 Subject: [PATCH 7/7] =?UTF-8?q?swaggeri=C3=B1o=202?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../octopus/internal/common/models/Entry.kt | 11 +++++ .../web/controllers/EntryController.kt | 40 ++++++++++++++++++- .../internal/web/utils/dtos/EntryDto.kt | 18 +++++++++ 3 files changed, 68 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/org/octopus/internal/common/models/Entry.kt b/src/main/kotlin/org/octopus/internal/common/models/Entry.kt index f9ed569..6a36b48 100755 --- a/src/main/kotlin/org/octopus/internal/common/models/Entry.kt +++ b/src/main/kotlin/org/octopus/internal/common/models/Entry.kt @@ -2,21 +2,32 @@ package org.octopus.internal.common.models import com.fasterxml.jackson.annotation.JsonGetter import com.fasterxml.jackson.annotation.JsonIgnore +import io.swagger.v3.oas.annotations.media.Schema import org.octopus.internal.common.enums.EEntryType import java.time.Month import java.util.* data class Entry( + @field:Schema(description = "The id of the entry in the DB") var id: Long?, + @field:Schema(description = "The name of the entry (between 1 and 50 characters long).") val name: String, + @field:Schema(description = "The type of entry (INCOME, OUTCOME, INVESTMENT).") val type: EEntryType, + @field:Schema(description = "The amount of the entry (greater than 0.00).") val amount: Double, + @field:Schema(description = "IF NOT recurrent: when is the entry being counted.") val fixedDate: Date? = null, + @field:Schema(description = "IF recurrent: must be true") val recurrent: Boolean? = false, + @field:Schema(description = "IF recurrent: list of month in which the event is repeated (1=JAN, 12=DEC)") @get:JsonIgnore val recurrentMonths: List? = mutableListOf(), + @field:Schema(description = "IF recurrent: starting date of the repetition") val startDate: Date? = null, + @field:Schema(description = "IF recurrent: ending date of the repetition") val endDate: Date? = null, + @field:Schema(description = "Color of the entry in the UI") val color: String ){ diff --git a/src/main/kotlin/org/octopus/internal/web/controllers/EntryController.kt b/src/main/kotlin/org/octopus/internal/web/controllers/EntryController.kt index 1ddb67c..99fab93 100755 --- a/src/main/kotlin/org/octopus/internal/web/controllers/EntryController.kt +++ b/src/main/kotlin/org/octopus/internal/web/controllers/EntryController.kt @@ -1,5 +1,8 @@ package org.octopus.internal.web.controllers +import io.swagger.v3.oas.annotations.Operation +import io.swagger.v3.oas.annotations.responses.ApiResponse +import io.swagger.v3.oas.annotations.tags.Tag import jakarta.validation.Valid import lombok.AllArgsConstructor import org.octopus.internal.common.models.Entry @@ -12,10 +15,17 @@ import org.springframework.web.bind.annotation.* @RestController @RequestMapping("/entries") @AllArgsConstructor +@Tag(name = "Entries Management", description = "Operations related to entries.") class EntryController( val entryService: EntryService ) { - + @Operation( + summary = "Create a new planning entry", + description = "Adds a new income, outcome, or investment entry to the system.", + responses = [ + ApiResponse(responseCode = "200", description = "Entry created successfully. Returned with ID") + ] + ) @PostMapping fun createEntry( @Valid @RequestBody entryDto: EntryDto @@ -23,21 +33,49 @@ class EntryController( return WebResponse.ok(entryService.createEntry(entryDto)) } + @Operation( + summary = "Get all entries in the DB", + description = "Returns a list containing all entries.", + responses = [ + ApiResponse(responseCode = "200", description = "A valid list.") + ] + ) @GetMapping fun getAllEntries(): WebResponse> { return WebResponse.ok(entryService.getAllEntries()) } + @Operation( + summary = "Delete all entries in the DB", + description = "Returns the result of the operation.", + responses = [ + ApiResponse(responseCode = "200", description = "Whether the operation succeeded.") + ] + ) @DeleteMapping fun deleteAllEntries(): WebResponse { return WebResponse.ok(entryService.deleteAll()) } + @Operation( + summary = "Delete a specific entry in the DB", + description = "Returns the result of the operation.", + responses = [ + ApiResponse(responseCode = "200", description = "Whether the operation succeeded.") + ] + ) @DeleteMapping("/{id}") fun deleteEntryById(@PathVariable("id") id: Long): WebResponse { return WebResponse.ok(entryService.deleteById(id)) } + @Operation( + summary = "Updates a specific entry in the DB", + description = "Returns the updated entry.", + responses = [ + ApiResponse(responseCode = "200", description = "The updated entry.") + ] + ) @PutMapping("/{id}") fun editEntryById(@PathVariable("id") id: Long, @Valid @RequestBody entryDto: EntryDto): WebResponse { diff --git a/src/main/kotlin/org/octopus/internal/web/utils/dtos/EntryDto.kt b/src/main/kotlin/org/octopus/internal/web/utils/dtos/EntryDto.kt index 290c8ab..9e356fa 100755 --- a/src/main/kotlin/org/octopus/internal/web/utils/dtos/EntryDto.kt +++ b/src/main/kotlin/org/octopus/internal/web/utils/dtos/EntryDto.kt @@ -1,5 +1,6 @@ package org.octopus.internal.web.utils.dtos +import io.swagger.v3.oas.annotations.media.Schema import jakarta.validation.constraints.DecimalMin import org.hibernate.validator.constraints.Length import org.octopus.internal.web.utils.dtos.validators.EntryHexColorValidator @@ -8,18 +9,35 @@ import org.octopus.internal.web.utils.dtos.validators.EntryTypeValidator import java.util.Date data class EntryDto( + @field:Schema(description = "The name of the entry (between 1 and 50 characters long).") @field:Length(min = 1, max = 50, message = "The name must be between 1 and 50 characters long.") val name: String, + + @field:Schema(description = "The type of entry (INCOME, OUTCOME, INVESTMENT).") @field:EntryTypeValidator.Validate val type: String, + + @field:Schema(description = "The amount of the entry (greater than 0.00).") @field:DecimalMin(value="0.00", inclusive = false, message = "The amount must be greater than 0.00.") val amount: Double, + + @field:Schema(description = "IF NOT recurrent: when is the entry being counted.") val fixedDate: Date?, + + @field:Schema(description = "IF recurrent: must be true") val recurrent: Boolean?, + + @field:Schema(description = "IF recurrent: list of month in which the event is repeated (1=JAN, 12=DEC)") @field:EntryMonthValidator.Validate val recurrentMonths: List?, + + @field:Schema(description = "IF recurrent: starting date of the repetition") val startDate: Date?, + + @field:Schema(description = "IF recurrent: ending date of the repetition") val endDate: Date?, + + @field:Schema(description = "Color of the entry in the UI") @field:EntryHexColorValidator.Validate val color: String = "0xFFFFFF" ) \ No newline at end of file -- 2.40.1