feat: Add ktlint to backend and format all files

This commit is contained in:
2025-10-31 10:25:12 +01:00
parent be5017fcd4
commit 841341857d
53 changed files with 496 additions and 402 deletions

View File

@@ -0,0 +1,29 @@
root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = space
insert_final_newline = true
max_line_length = 120
tab_width = 4
trim_trailing_whitespace = true
[*.{kt,kts}]
indent_size = 4
max_line_length = 120
ij_kotlin_allow_trailing_comma = true
ij_kotlin_allow_trailing_comma_on_call_site = true
ktlint_standard_package-name = disabled
[*.{yaml,yml}]
indent_size = 2
[*.gradle]
indent_size = 4
[*.md]
trim_trailing_whitespace = false

View File

@@ -5,6 +5,7 @@ plugins {
id 'io.spring.dependency-management' version '1.1.7' id 'io.spring.dependency-management' version '1.1.7'
id 'org.jetbrains.kotlin.plugin.jpa' version '1.9.25' id 'org.jetbrains.kotlin.plugin.jpa' version '1.9.25'
id 'org.openapi.generator' version '7.11.0' id 'org.openapi.generator' version '7.11.0'
id 'org.jlleitschuh.gradle.ktlint' version '13.1.0'
} }
group = 'com.betriebsratkanzlei' group = 'com.betriebsratkanzlei'
@@ -24,12 +25,6 @@ ext {
openHtmlVersion = '1.0.10' openHtmlVersion = '1.0.10'
} }
//dependencyManagement {
// imports {
// mavenBom 'eu.europa.ec.joinup.sd-dss:dss-bom:6.2'
// }
//}
dependencies { dependencies {
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'
@@ -69,6 +64,16 @@ allOpen {
annotation 'jakarta.persistence.Embeddable' annotation 'jakarta.persistence.Embeddable'
} }
ktlint {
version = "1.5.0"
android = false
ignoreFailures = false
filter {
exclude("**/generated/**")
exclude { element -> element.file.path.contains("generated/") }
}
}
def generatedSourcesServerLegalconsenthubDir = "$buildDir/generated/server".toString() def generatedSourcesServerLegalconsenthubDir = "$buildDir/generated/server".toString()
sourceSets { sourceSets {

View File

@@ -3,18 +3,17 @@ package com.betriebsratkanzlei.legalconsenthub.application_form
import com.betriebsratkanzlei.legalconsenthub.form_element.FormElementSection import com.betriebsratkanzlei.legalconsenthub.form_element.FormElementSection
import com.betriebsratkanzlei.legalconsenthub.user.User import com.betriebsratkanzlei.legalconsenthub.user.User
import com.betriebsratkanzlei.legalconsenthub_api.model.ApplicationFormStatus import com.betriebsratkanzlei.legalconsenthub_api.model.ApplicationFormStatus
import jakarta.persistence.JoinColumn
import jakarta.persistence.ManyToOne
import jakarta.persistence.CascadeType import jakarta.persistence.CascadeType
import jakarta.persistence.Column import jakarta.persistence.Column
import jakarta.persistence.Entity import jakarta.persistence.Entity
import jakarta.persistence.EntityListeners import jakarta.persistence.EntityListeners
import jakarta.persistence.Enumerated
import jakarta.persistence.EnumType import jakarta.persistence.EnumType
import jakarta.persistence.Enumerated
import jakarta.persistence.GeneratedValue import jakarta.persistence.GeneratedValue
import jakarta.persistence.Id import jakarta.persistence.Id
import jakarta.persistence.JoinColumn
import jakarta.persistence.ManyToOne
import jakarta.persistence.OneToMany import jakarta.persistence.OneToMany
import org.springframework.data.annotation.CreatedDate import org.springframework.data.annotation.CreatedDate
import org.springframework.data.annotation.LastModifiedDate import org.springframework.data.annotation.LastModifiedDate
import org.springframework.data.jpa.domain.support.AuditingEntityListener import org.springframework.data.jpa.domain.support.AuditingEntityListener
@@ -27,35 +26,26 @@ class ApplicationForm(
@Id @Id
@GeneratedValue @GeneratedValue
var id: UUID? = null, var id: UUID? = null,
@Column(nullable = false) @Column(nullable = false)
var name: String = "", var name: String = "",
@OneToMany(mappedBy = "applicationForm", cascade = [CascadeType.ALL], orphanRemoval = true) @OneToMany(mappedBy = "applicationForm", cascade = [CascadeType.ALL], orphanRemoval = true)
var formElementSections: MutableList<FormElementSection> = mutableListOf(), var formElementSections: MutableList<FormElementSection> = mutableListOf(),
@Column(nullable = false) @Column(nullable = false)
var isTemplate: Boolean, var isTemplate: Boolean,
var organizationId: String = "", var organizationId: String = "",
@Enumerated(EnumType.STRING) @Enumerated(EnumType.STRING)
@Column(nullable = false) @Column(nullable = false)
var status: ApplicationFormStatus = ApplicationFormStatus.DRAFT, var status: ApplicationFormStatus = ApplicationFormStatus.DRAFT,
@ManyToOne @ManyToOne
@JoinColumn(name = "created_by_id", nullable = false) @JoinColumn(name = "created_by_id", nullable = false)
var createdBy: User, var createdBy: User,
@ManyToOne @ManyToOne
@JoinColumn(name = "last_modified_by_id", nullable = false) @JoinColumn(name = "last_modified_by_id", nullable = false)
var lastModifiedBy: User, var lastModifiedBy: User,
@CreatedDate @CreatedDate
@Column(nullable = false) @Column(nullable = false)
var createdAt: LocalDateTime? = null, var createdAt: LocalDateTime? = null,
@LastModifiedDate @LastModifiedDate
@Column(nullable = false) @Column(nullable = false)
var modifiedAt: LocalDateTime? = null var modifiedAt: LocalDateTime? = null,
) )

View File

@@ -18,80 +18,94 @@ class ApplicationFormController(
val applicationFormService: ApplicationFormService, val applicationFormService: ApplicationFormService,
val pagedApplicationFormMapper: PagedApplicationFormMapper, val pagedApplicationFormMapper: PagedApplicationFormMapper,
val applicationFormMapper: ApplicationFormMapper, val applicationFormMapper: ApplicationFormMapper,
val applicationFormFormatService: ApplicationFormFormatService val applicationFormFormatService: ApplicationFormFormatService,
) : ApplicationFormApi { ) : ApplicationFormApi {
@PreAuthorize(
@PreAuthorize("hasAnyRole('CHIEF_EXECUTIVE_OFFICER', 'BUSINESS_DEPARTMENT', 'IT_DEPARTMENT', 'HUMAN_RESOURCES', 'HEAD_OF_WORKS_COUNCIL', 'WORKS_COUNCIL')") "hasAnyRole('CHIEF_EXECUTIVE_OFFICER', 'BUSINESS_DEPARTMENT', 'IT_DEPARTMENT', 'HUMAN_RESOURCES', 'HEAD_OF_WORKS_COUNCIL', 'WORKS_COUNCIL')",
override fun createApplicationForm(createApplicationFormDto: CreateApplicationFormDto): ResponseEntity<ApplicationFormDto> { )
override fun createApplicationForm(
createApplicationFormDto: CreateApplicationFormDto,
): ResponseEntity<ApplicationFormDto> {
val updatedCreateApplicationFormDto = createApplicationFormDto.copy(isTemplate = false) val updatedCreateApplicationFormDto = createApplicationFormDto.copy(isTemplate = false)
return ResponseEntity.ok( return ResponseEntity.ok(
applicationFormMapper.toApplicationFormDto( applicationFormMapper.toApplicationFormDto(
applicationFormService.createApplicationForm(updatedCreateApplicationFormDto) applicationFormService.createApplicationForm(updatedCreateApplicationFormDto),
) ),
) )
} }
@PreAuthorize("hasAnyRole('CHIEF_EXECUTIVE_OFFICER', 'BUSINESS_DEPARTMENT', 'IT_DEPARTMENT', 'HUMAN_RESOURCES', 'HEAD_OF_WORKS_COUNCIL', 'WORKS_COUNCIL', 'EMPLOYEE')") @PreAuthorize(
override fun getAllApplicationForms(organizationId: String?): ResponseEntity<PagedApplicationFormDto> { "hasAnyRole('CHIEF_EXECUTIVE_OFFICER', 'BUSINESS_DEPARTMENT', 'IT_DEPARTMENT', 'HUMAN_RESOURCES', 'HEAD_OF_WORKS_COUNCIL', 'WORKS_COUNCIL', 'EMPLOYEE')",
return ResponseEntity.ok( )
override fun getAllApplicationForms(organizationId: String?): ResponseEntity<PagedApplicationFormDto> =
ResponseEntity.ok(
pagedApplicationFormMapper.toPagedApplicationFormDto( pagedApplicationFormMapper.toPagedApplicationFormDto(
applicationFormService.getApplicationForms(organizationId) applicationFormService.getApplicationForms(organizationId),
),
) )
)
}
@PreAuthorize("hasAnyRole('CHIEF_EXECUTIVE_OFFICER', 'BUSINESS_DEPARTMENT', 'IT_DEPARTMENT', 'HUMAN_RESOURCES', 'HEAD_OF_WORKS_COUNCIL', 'WORKS_COUNCIL', 'EMPLOYEE')") @PreAuthorize(
override fun getApplicationFormById(id: UUID): ResponseEntity<ApplicationFormDto> { "hasAnyRole('CHIEF_EXECUTIVE_OFFICER', 'BUSINESS_DEPARTMENT', 'IT_DEPARTMENT', 'HUMAN_RESOURCES', 'HEAD_OF_WORKS_COUNCIL', 'WORKS_COUNCIL', 'EMPLOYEE')",
return ResponseEntity.ok( )
override fun getApplicationFormById(id: UUID): ResponseEntity<ApplicationFormDto> =
ResponseEntity.ok(
applicationFormMapper.toApplicationFormDto( applicationFormMapper.toApplicationFormDto(
applicationFormService.getApplicationFormById(id) applicationFormService.getApplicationFormById(id),
),
) )
)
}
@PreAuthorize("hasAnyRole('CHIEF_EXECUTIVE_OFFICER', 'BUSINESS_DEPARTMENT', 'IT_DEPARTMENT', 'HUMAN_RESOURCES', 'HEAD_OF_WORKS_COUNCIL', 'WORKS_COUNCIL', 'EMPLOYEE')") @PreAuthorize(
"hasAnyRole('CHIEF_EXECUTIVE_OFFICER', 'BUSINESS_DEPARTMENT', 'IT_DEPARTMENT', 'HUMAN_RESOURCES', 'HEAD_OF_WORKS_COUNCIL', 'WORKS_COUNCIL', 'EMPLOYEE')",
)
override fun getApplicationFormHtml(id: UUID): ResponseEntity<String> { override fun getApplicationFormHtml(id: UUID): ResponseEntity<String> {
val applicationForm = applicationFormService.getApplicationFormById(id) val applicationForm = applicationFormService.getApplicationFormById(id)
return ResponseEntity.ok( return ResponseEntity.ok(
applicationFormFormatService.generateHtml(applicationForm) applicationFormFormatService.generateHtml(applicationForm),
) )
} }
@PreAuthorize("hasAnyRole('CHIEF_EXECUTIVE_OFFICER', 'BUSINESS_DEPARTMENT', 'IT_DEPARTMENT', 'HUMAN_RESOURCES', 'HEAD_OF_WORKS_COUNCIL', 'WORKS_COUNCIL', 'EMPLOYEE')") @PreAuthorize(
"hasAnyRole('CHIEF_EXECUTIVE_OFFICER', 'BUSINESS_DEPARTMENT', 'IT_DEPARTMENT', 'HUMAN_RESOURCES', 'HEAD_OF_WORKS_COUNCIL', 'WORKS_COUNCIL', 'EMPLOYEE')",
)
override fun getApplicationFormPdf(id: UUID): ResponseEntity<Resource> { override fun getApplicationFormPdf(id: UUID): ResponseEntity<Resource> {
val applicationForm = applicationFormService.getApplicationFormById(id) val applicationForm = applicationFormService.getApplicationFormById(id)
val pdfBytes = applicationFormFormatService.generatePdf(applicationForm) val pdfBytes = applicationFormFormatService.generatePdf(applicationForm)
val resource = ByteArrayResource(pdfBytes) val resource = ByteArrayResource(pdfBytes)
return ResponseEntity.ok() return ResponseEntity
.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "inline; filename=\"form-$id.pdf\"") .header(HttpHeaders.CONTENT_DISPOSITION, "inline; filename=\"form-$id.pdf\"")
.contentType(MediaType.APPLICATION_PDF) .contentType(MediaType.APPLICATION_PDF)
.body(resource) .body(resource)
} }
@PreAuthorize("hasAnyRole('CHIEF_EXECUTIVE_OFFICER', 'BUSINESS_DEPARTMENT', 'IT_DEPARTMENT', 'HUMAN_RESOURCES', 'HEAD_OF_WORKS_COUNCIL', 'WORKS_COUNCIL')") @PreAuthorize(
"hasAnyRole('CHIEF_EXECUTIVE_OFFICER', 'BUSINESS_DEPARTMENT', 'IT_DEPARTMENT', 'HUMAN_RESOURCES', 'HEAD_OF_WORKS_COUNCIL', 'WORKS_COUNCIL')",
)
override fun updateApplicationForm( override fun updateApplicationForm(
id: UUID, id: UUID,
applicationFormDto: ApplicationFormDto applicationFormDto: ApplicationFormDto,
): ResponseEntity<ApplicationFormDto> { ): ResponseEntity<ApplicationFormDto> =
return ResponseEntity.ok( ResponseEntity.ok(
applicationFormMapper.toApplicationFormDto( applicationFormMapper.toApplicationFormDto(
applicationFormService.updateApplicationForm(applicationFormDto) applicationFormService.updateApplicationForm(applicationFormDto),
),
) )
)
}
@PreAuthorize("hasAnyRole('CHIEF_EXECUTIVE_OFFICER', 'BUSINESS_DEPARTMENT', 'IT_DEPARTMENT', 'HUMAN_RESOURCES', 'HEAD_OF_WORKS_COUNCIL', 'WORKS_COUNCIL')") @PreAuthorize(
"hasAnyRole('CHIEF_EXECUTIVE_OFFICER', 'BUSINESS_DEPARTMENT', 'IT_DEPARTMENT', 'HUMAN_RESOURCES', 'HEAD_OF_WORKS_COUNCIL', 'WORKS_COUNCIL')",
)
override fun deleteApplicationForm(id: UUID): ResponseEntity<Unit> { override fun deleteApplicationForm(id: UUID): ResponseEntity<Unit> {
applicationFormService.deleteApplicationFormByID(id) applicationFormService.deleteApplicationFormByID(id)
return ResponseEntity.noContent().build() return ResponseEntity.noContent().build()
} }
@PreAuthorize("hasAnyRole('CHIEF_EXECUTIVE_OFFICER', 'BUSINESS_DEPARTMENT', 'IT_DEPARTMENT', 'HUMAN_RESOURCES', 'HEAD_OF_WORKS_COUNCIL', 'WORKS_COUNCIL')") @PreAuthorize(
override fun submitApplicationForm(id: UUID): ResponseEntity<ApplicationFormDto> { "hasAnyRole('CHIEF_EXECUTIVE_OFFICER', 'BUSINESS_DEPARTMENT', 'IT_DEPARTMENT', 'HUMAN_RESOURCES', 'HEAD_OF_WORKS_COUNCIL', 'WORKS_COUNCIL')",
return ResponseEntity.ok( )
override fun submitApplicationForm(id: UUID): ResponseEntity<ApplicationFormDto> =
ResponseEntity.ok(
applicationFormMapper.toApplicationFormDto( applicationFormMapper.toApplicationFormDto(
applicationFormService.submitApplicationForm(id) applicationFormService.submitApplicationForm(id),
),
) )
)
}
} }

View File

@@ -1,20 +1,21 @@
package com.betriebsratkanzlei.legalconsenthub.application_form package com.betriebsratkanzlei.legalconsenthub.application_form
import com.openhtmltopdf.pdfboxout.PdfRendererBuilder
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
import org.thymeleaf.TemplateEngine import org.thymeleaf.TemplateEngine
import org.thymeleaf.context.Context import org.thymeleaf.context.Context
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import com.openhtmltopdf.pdfboxout.PdfRendererBuilder
@Service @Service
class ApplicationFormFormatService( class ApplicationFormFormatService(
private val templateEngine: TemplateEngine private val templateEngine: TemplateEngine,
) { ) {
fun generatePdf(applicationForm: ApplicationForm): ByteArray { fun generatePdf(applicationForm: ApplicationForm): ByteArray {
val htmlContent = generateHtml(applicationForm) val htmlContent = generateHtml(applicationForm)
val outputStream = ByteArrayOutputStream() val outputStream = ByteArrayOutputStream()
PdfRendererBuilder().useFastMode() PdfRendererBuilder()
.useFastMode()
.withHtmlContent(htmlContent, null) .withHtmlContent(htmlContent, null)
.toStream(outputStream) .toStream(outputStream)
.run() .run()
@@ -23,7 +24,8 @@ class ApplicationFormFormatService(
} }
fun generateHtml(applicationForm: ApplicationForm): String { fun generateHtml(applicationForm: ApplicationForm): String {
val context = Context().apply { val context =
Context().apply {
setVariable("applicationForm", applicationForm) setVariable("applicationForm", applicationForm)
} }
return templateEngine.process("application_form_template", context) return templateEngine.process("application_form_template", context)

View File

@@ -1,7 +1,6 @@
package com.betriebsratkanzlei.legalconsenthub.application_form package com.betriebsratkanzlei.legalconsenthub.application_form
import com.betriebsratkanzlei.legalconsenthub.form_element.FormElementSectionMapper import com.betriebsratkanzlei.legalconsenthub.form_element.FormElementSectionMapper
import com.betriebsratkanzlei.legalconsenthub.user.User
import com.betriebsratkanzlei.legalconsenthub.user.UserMapper import com.betriebsratkanzlei.legalconsenthub.user.UserMapper
import com.betriebsratkanzlei.legalconsenthub.user.UserService import com.betriebsratkanzlei.legalconsenthub.user.UserService
import com.betriebsratkanzlei.legalconsenthub_api.model.ApplicationFormDto import com.betriebsratkanzlei.legalconsenthub_api.model.ApplicationFormDto
@@ -13,50 +12,62 @@ import java.time.LocalDateTime
class ApplicationFormMapper( class ApplicationFormMapper(
private val formElementSectionMapper: FormElementSectionMapper, private val formElementSectionMapper: FormElementSectionMapper,
private val userMapper: UserMapper, private val userMapper: UserMapper,
private val userService: UserService private val userService: UserService,
) { ) {
fun toApplicationFormDto(applicationForm: ApplicationForm): ApplicationFormDto { fun toApplicationFormDto(applicationForm: ApplicationForm): ApplicationFormDto =
return ApplicationFormDto( ApplicationFormDto(
id = applicationForm.id ?: throw IllegalStateException("ApplicationForm ID must not be null!"), id = applicationForm.id ?: throw IllegalStateException("ApplicationForm ID must not be null!"),
name = applicationForm.name, name = applicationForm.name,
formElementSections = applicationForm.formElementSections.map { formElementSectionMapper.toFormElementSectionDto(it) }, formElementSections =
applicationForm.formElementSections.map {
formElementSectionMapper
.toFormElementSectionDto(
it,
)
},
isTemplate = applicationForm.isTemplate, isTemplate = applicationForm.isTemplate,
organizationId = applicationForm.organizationId, organizationId = applicationForm.organizationId,
createdBy = userMapper.toUserDto(applicationForm.createdBy), createdBy = userMapper.toUserDto(applicationForm.createdBy),
lastModifiedBy = userMapper.toUserDto(applicationForm.lastModifiedBy), lastModifiedBy = userMapper.toUserDto(applicationForm.lastModifiedBy),
createdAt = applicationForm.createdAt ?: LocalDateTime.now(), createdAt = applicationForm.createdAt ?: LocalDateTime.now(),
modifiedAt = applicationForm.modifiedAt ?: LocalDateTime.now(), modifiedAt = applicationForm.modifiedAt ?: LocalDateTime.now(),
status = applicationForm.status status = applicationForm.status,
) )
}
fun toApplicationForm(applicationForm: ApplicationFormDto): ApplicationForm { fun toApplicationForm(applicationForm: ApplicationFormDto): ApplicationForm =
return ApplicationForm( ApplicationForm(
id = applicationForm.id, id = applicationForm.id,
name = applicationForm.name, name = applicationForm.name,
formElementSections = applicationForm.formElementSections.map { formElementSectionMapper.toFormElementSection(it) }.toMutableList(), formElementSections =
applicationForm.formElementSections
.map {
formElementSectionMapper.toFormElementSection(it)
}.toMutableList(),
isTemplate = applicationForm.isTemplate, isTemplate = applicationForm.isTemplate,
organizationId = applicationForm.organizationId, organizationId = applicationForm.organizationId,
status = applicationForm.status, status = applicationForm.status,
createdBy = userMapper.toUser(applicationForm.createdBy), createdBy = userMapper.toUser(applicationForm.createdBy),
lastModifiedBy = userMapper.toUser(applicationForm.lastModifiedBy), lastModifiedBy = userMapper.toUser(applicationForm.lastModifiedBy),
createdAt = applicationForm.createdAt, createdAt = applicationForm.createdAt,
modifiedAt = applicationForm.modifiedAt modifiedAt = applicationForm.modifiedAt,
) )
}
fun toApplicationForm(createApplicationFormDto: CreateApplicationFormDto): ApplicationForm { fun toApplicationForm(createApplicationFormDto: CreateApplicationFormDto): ApplicationForm {
val currentUser = userService.getCurrentUser() val currentUser = userService.getCurrentUser()
val applicationForm = ApplicationForm( val applicationForm =
ApplicationForm(
name = createApplicationFormDto.name, name = createApplicationFormDto.name,
isTemplate = createApplicationFormDto.isTemplate, isTemplate = createApplicationFormDto.isTemplate,
organizationId = createApplicationFormDto.organizationId ?: "", organizationId = createApplicationFormDto.organizationId ?: "",
status = createApplicationFormDto.status ?: com.betriebsratkanzlei.legalconsenthub_api.model.ApplicationFormStatus.DRAFT, status =
createApplicationFormDto.status
?: com.betriebsratkanzlei.legalconsenthub_api.model.ApplicationFormStatus.DRAFT,
createdBy = currentUser, createdBy = currentUser,
lastModifiedBy = currentUser, lastModifiedBy = currentUser,
) )
applicationForm.formElementSections = createApplicationFormDto.formElementSections applicationForm.formElementSections =
createApplicationFormDto.formElementSections
.map { formElementSectionMapper.toFormElementSection(it, applicationForm) } .map { formElementSectionMapper.toFormElementSection(it, applicationForm) }
.toMutableList() .toMutableList()
return applicationForm return applicationForm

View File

@@ -11,6 +11,11 @@ import java.util.UUID
interface ApplicationFormRepository : JpaRepository<ApplicationForm, UUID> { interface ApplicationFormRepository : JpaRepository<ApplicationForm, UUID> {
fun findAllByIsTemplateTrue(page: Pageable): Page<ApplicationForm> fun findAllByIsTemplateTrue(page: Pageable): Page<ApplicationForm>
@Query("SELECT c FROM ApplicationForm c WHERE (c.isTemplate IS false) AND (:organizationId is null or c.organizationId = :organizationId)") @Query(
fun findAllByIsTemplateFalseAndOrganizationId(organizationId: String?, page: Pageable): Page<ApplicationForm> "SELECT c FROM ApplicationForm c WHERE (c.isTemplate IS false) AND (:organizationId is null or c.organizationId = :organizationId)",
)
fun findAllByIsTemplateFalseAndOrganizationId(
organizationId: String?,
page: Pageable,
): Page<ApplicationForm>
} }

View File

@@ -5,15 +5,14 @@ import com.betriebsratkanzlei.legalconsenthub.error.ApplicationFormNotCreatedExc
import com.betriebsratkanzlei.legalconsenthub.error.ApplicationFormNotDeletedException import com.betriebsratkanzlei.legalconsenthub.error.ApplicationFormNotDeletedException
import com.betriebsratkanzlei.legalconsenthub.error.ApplicationFormNotFoundException import com.betriebsratkanzlei.legalconsenthub.error.ApplicationFormNotFoundException
import com.betriebsratkanzlei.legalconsenthub.error.ApplicationFormNotUpdatedException import com.betriebsratkanzlei.legalconsenthub.error.ApplicationFormNotUpdatedException
// import com.betriebsratkanzlei.legalconsenthub.notification.NotificationService
import com.betriebsratkanzlei.legalconsenthub_api.model.ApplicationFormDto import com.betriebsratkanzlei.legalconsenthub_api.model.ApplicationFormDto
import com.betriebsratkanzlei.legalconsenthub_api.model.ApplicationFormStatus import com.betriebsratkanzlei.legalconsenthub_api.model.ApplicationFormStatus
import com.betriebsratkanzlei.legalconsenthub_api.model.CreateApplicationFormDto import com.betriebsratkanzlei.legalconsenthub_api.model.CreateApplicationFormDto
import com.betriebsratkanzlei.legalconsenthub_api.model.NotificationType
import org.springframework.data.domain.Page import org.springframework.data.domain.Page
import org.springframework.data.domain.PageRequest import org.springframework.data.domain.PageRequest
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
import java.util.UUID import java.util.UUID
// import com.betriebsratkanzlei.legalconsenthub.notification.NotificationService
@Service @Service
class ApplicationFormService( class ApplicationFormService(
@@ -21,7 +20,6 @@ class ApplicationFormService(
private val applicationFormMapper: ApplicationFormMapper, private val applicationFormMapper: ApplicationFormMapper,
// private val notificationService: NotificationService // private val notificationService: NotificationService
) { ) {
fun createApplicationForm(createApplicationFormDto: CreateApplicationFormDto): ApplicationForm { fun createApplicationForm(createApplicationFormDto: CreateApplicationFormDto): ApplicationForm {
val applicationForm = applicationFormMapper.toApplicationForm(createApplicationFormDto) val applicationForm = applicationFormMapper.toApplicationForm(createApplicationFormDto)
val savedApplicationForm: ApplicationForm val savedApplicationForm: ApplicationForm
@@ -34,8 +32,9 @@ class ApplicationFormService(
return savedApplicationForm return savedApplicationForm
} }
fun getApplicationFormById(id: UUID): ApplicationForm { fun getApplicationFormById(id: UUID): ApplicationForm =
return applicationFormRepository.findById(id).orElseThrow { ApplicationFormNotFoundException(id) } applicationFormRepository.findById(id).orElseThrow {
ApplicationFormNotFoundException(id)
} }
fun getApplicationForms(organizationId: String?): Page<ApplicationForm> { fun getApplicationForms(organizationId: String?): Page<ApplicationForm> {
@@ -73,13 +72,14 @@ class ApplicationFormService(
applicationFormId = id, applicationFormId = id,
currentState = applicationForm.status, currentState = applicationForm.status,
expectedState = ApplicationFormStatus.DRAFT, expectedState = ApplicationFormStatus.DRAFT,
operation = "submit" operation = "submit",
) )
} }
applicationForm.status = ApplicationFormStatus.SUBMITTED applicationForm.status = ApplicationFormStatus.SUBMITTED
val savedApplicationForm = try { val savedApplicationForm =
try {
applicationFormRepository.save(applicationForm) applicationFormRepository.save(applicationForm)
} catch (e: Exception) { } catch (e: Exception) {
throw ApplicationFormNotUpdatedException(e, id) throw ApplicationFormNotUpdatedException(e, id)
@@ -93,7 +93,9 @@ class ApplicationFormService(
private fun createSubmissionNotifications(applicationForm: ApplicationForm) { private fun createSubmissionNotifications(applicationForm: ApplicationForm) {
val title = "Neuer Mitbestimmungsantrag eingereicht" val title = "Neuer Mitbestimmungsantrag eingereicht"
val message = "Ein neuer Mitbestimmungsantrag '${applicationForm.name}' wurde von ${applicationForm.createdBy.name} eingereicht und wartet auf Ihre Bearbeitung." val message =
"Ein neuer Mitbestimmungsantrag '${applicationForm.name}' wurde von " +
"${applicationForm.createdBy.name} eingereicht und wartet auf Ihre Bearbeitung."
val clickTarget = "/application-forms/${applicationForm.id}/0" val clickTarget = "/application-forms/${applicationForm.id}/0"
// // Create separate notification for each role that should be notified // // Create separate notification for each role that should be notified

View File

@@ -1,15 +1,15 @@
package com.betriebsratkanzlei.legalconsenthub.application_form package com.betriebsratkanzlei.legalconsenthub.application_form
import com.betriebsratkanzlei.legalconsenthub_api.model.ApplicationFormDto
import com.betriebsratkanzlei.legalconsenthub_api.model.CreateApplicationFormDto
import com.betriebsratkanzlei.legalconsenthub_api.model.PagedApplicationFormDto import com.betriebsratkanzlei.legalconsenthub_api.model.PagedApplicationFormDto
import org.springframework.data.domain.Page import org.springframework.data.domain.Page
import org.springframework.stereotype.Component import org.springframework.stereotype.Component
@Component @Component
class PagedApplicationFormMapper(private val applicationFormMapper: ApplicationFormMapper) { class PagedApplicationFormMapper(
fun toPagedApplicationFormDto(pagedApplicationForm: Page<ApplicationForm>): PagedApplicationFormDto { private val applicationFormMapper: ApplicationFormMapper,
return PagedApplicationFormDto( ) {
fun toPagedApplicationFormDto(pagedApplicationForm: Page<ApplicationForm>): PagedApplicationFormDto =
PagedApplicationFormDto(
content = pagedApplicationForm.content.map { applicationFormMapper.toApplicationFormDto(it) }, content = pagedApplicationForm.content.map { applicationFormMapper.toApplicationFormDto(it) },
number = pagedApplicationForm.number, number = pagedApplicationForm.number,
propertySize = pagedApplicationForm.size, propertySize = pagedApplicationForm.size,
@@ -20,5 +20,4 @@ class PagedApplicationFormMapper(private val applicationFormMapper: ApplicationF
empty = pagedApplicationForm.isEmpty, empty = pagedApplicationForm.isEmpty,
first = pagedApplicationForm.isFirst, first = pagedApplicationForm.isFirst,
) )
}
} }

View File

@@ -17,47 +17,54 @@ class ApplicationFormTemplateController(
val pagedApplicationFormMapper: PagedApplicationFormMapper, val pagedApplicationFormMapper: PagedApplicationFormMapper,
val applicationFormMapper: ApplicationFormMapper, val applicationFormMapper: ApplicationFormMapper,
) : ApplicationFormTemplateApi { ) : ApplicationFormTemplateApi {
@PreAuthorize(
@PreAuthorize("hasAnyRole('CHIEF_EXECUTIVE_OFFICER', 'BUSINESS_DEPARTMENT', 'IT_DEPARTMENT', 'HUMAN_RESOURCES', 'HEAD_OF_WORKS_COUNCIL', 'WORKS_COUNCIL')") "hasAnyRole('CHIEF_EXECUTIVE_OFFICER', 'BUSINESS_DEPARTMENT', 'IT_DEPARTMENT', 'HUMAN_RESOURCES', 'HEAD_OF_WORKS_COUNCIL', 'WORKS_COUNCIL')",
override fun createApplicationFormTemplate(createApplicationFormDto: CreateApplicationFormDto): ResponseEntity<ApplicationFormDto> { )
return ResponseEntity.ok( override fun createApplicationFormTemplate(
createApplicationFormDto: CreateApplicationFormDto,
): ResponseEntity<ApplicationFormDto> =
ResponseEntity.ok(
applicationFormMapper.toApplicationFormDto( applicationFormMapper.toApplicationFormDto(
applicationFormTemplateService.createApplicationFormTemplate(createApplicationFormDto) applicationFormTemplateService.createApplicationFormTemplate(createApplicationFormDto),
),
) )
)
}
@PreAuthorize("hasAnyRole('CHIEF_EXECUTIVE_OFFICER', 'BUSINESS_DEPARTMENT', 'IT_DEPARTMENT', 'HUMAN_RESOURCES', 'HEAD_OF_WORKS_COUNCIL', 'WORKS_COUNCIL')") @PreAuthorize(
override fun getAllApplicationFormTemplates(): ResponseEntity<PagedApplicationFormDto> { "hasAnyRole('CHIEF_EXECUTIVE_OFFICER', 'BUSINESS_DEPARTMENT', 'IT_DEPARTMENT', 'HUMAN_RESOURCES', 'HEAD_OF_WORKS_COUNCIL', 'WORKS_COUNCIL')",
return ResponseEntity.ok( )
override fun getAllApplicationFormTemplates(): ResponseEntity<PagedApplicationFormDto> =
ResponseEntity.ok(
pagedApplicationFormMapper.toPagedApplicationFormDto( pagedApplicationFormMapper.toPagedApplicationFormDto(
applicationFormTemplateService.getApplicationFormTemplates() applicationFormTemplateService.getApplicationFormTemplates(),
),
) )
)
}
@PreAuthorize("hasAnyRole('CHIEF_EXECUTIVE_OFFICER', 'BUSINESS_DEPARTMENT', 'IT_DEPARTMENT', 'HUMAN_RESOURCES', 'HEAD_OF_WORKS_COUNCIL', 'WORKS_COUNCIL')") @PreAuthorize(
override fun getApplicationFormTemplateById(id: UUID): ResponseEntity<ApplicationFormDto> { "hasAnyRole('CHIEF_EXECUTIVE_OFFICER', 'BUSINESS_DEPARTMENT', 'IT_DEPARTMENT', 'HUMAN_RESOURCES', 'HEAD_OF_WORKS_COUNCIL', 'WORKS_COUNCIL')",
return ResponseEntity.ok( )
override fun getApplicationFormTemplateById(id: UUID): ResponseEntity<ApplicationFormDto> =
ResponseEntity.ok(
applicationFormMapper.toApplicationFormDto( applicationFormMapper.toApplicationFormDto(
applicationFormTemplateService.getApplicationFormTemplateById(id) applicationFormTemplateService.getApplicationFormTemplateById(id),
),
) )
)
}
@PreAuthorize("hasAnyRole('CHIEF_EXECUTIVE_OFFICER', 'BUSINESS_DEPARTMENT', 'IT_DEPARTMENT', 'HUMAN_RESOURCES', 'HEAD_OF_WORKS_COUNCIL', 'WORKS_COUNCIL')") @PreAuthorize(
"hasAnyRole('CHIEF_EXECUTIVE_OFFICER', 'BUSINESS_DEPARTMENT', 'IT_DEPARTMENT', 'HUMAN_RESOURCES', 'HEAD_OF_WORKS_COUNCIL', 'WORKS_COUNCIL')",
)
override fun updateApplicationFormTemplate( override fun updateApplicationFormTemplate(
id: UUID, id: UUID,
applicationFormDto: ApplicationFormDto applicationFormDto: ApplicationFormDto,
): ResponseEntity<ApplicationFormDto> { ): ResponseEntity<ApplicationFormDto> =
return ResponseEntity.ok( ResponseEntity.ok(
applicationFormMapper.toApplicationFormDto( applicationFormMapper.toApplicationFormDto(
applicationFormTemplateService.updateApplicationFormTemplate(applicationFormDto) applicationFormTemplateService.updateApplicationFormTemplate(applicationFormDto),
),
) )
)
}
@PreAuthorize("hasAnyRole('CHIEF_EXECUTIVE_OFFICER', 'BUSINESS_DEPARTMENT', 'IT_DEPARTMENT', 'HUMAN_RESOURCES', 'HEAD_OF_WORKS_COUNCIL', 'WORKS_COUNCIL')") @PreAuthorize(
"hasAnyRole('CHIEF_EXECUTIVE_OFFICER', 'BUSINESS_DEPARTMENT', 'IT_DEPARTMENT', 'HUMAN_RESOURCES', 'HEAD_OF_WORKS_COUNCIL', 'WORKS_COUNCIL')",
)
override fun deleteApplicationFormTemplate(id: UUID): ResponseEntity<Unit> { override fun deleteApplicationFormTemplate(id: UUID): ResponseEntity<Unit> {
applicationFormTemplateService.deleteApplicationFormTemplateByID(id) applicationFormTemplateService.deleteApplicationFormTemplateByID(id)
return ResponseEntity.noContent().build() return ResponseEntity.noContent().build()

View File

@@ -17,9 +17,8 @@ import java.util.UUID
@Service @Service
class ApplicationFormTemplateService( class ApplicationFormTemplateService(
private val applicationFormRepository: ApplicationFormRepository, private val applicationFormRepository: ApplicationFormRepository,
private val applicationFormMapper: ApplicationFormMapper private val applicationFormMapper: ApplicationFormMapper,
) { ) {
fun createApplicationFormTemplate(createApplicationFormDto: CreateApplicationFormDto): ApplicationForm { fun createApplicationFormTemplate(createApplicationFormDto: CreateApplicationFormDto): ApplicationForm {
val applicationForm = applicationFormMapper.toApplicationForm(createApplicationFormDto) val applicationForm = applicationFormMapper.toApplicationForm(createApplicationFormDto)
val savedApplicationForm: ApplicationForm val savedApplicationForm: ApplicationForm
@@ -32,8 +31,9 @@ class ApplicationFormTemplateService(
return savedApplicationForm return savedApplicationForm
} }
fun getApplicationFormTemplateById(id: UUID): ApplicationForm { fun getApplicationFormTemplateById(id: UUID): ApplicationForm =
return applicationFormRepository.findById(id).orElseThrow { ApplicationFormNotFoundException(id) } applicationFormRepository.findById(id).orElseThrow {
ApplicationFormNotFoundException(id)
} }
fun getApplicationFormTemplates(): Page<ApplicationForm> { fun getApplicationFormTemplates(): Page<ApplicationForm> {

View File

@@ -1,4 +1,4 @@
package com.betriebsratkanzlei.legalconsenthub.comment; package com.betriebsratkanzlei.legalconsenthub.comment
import com.betriebsratkanzlei.legalconsenthub.application_form.ApplicationForm import com.betriebsratkanzlei.legalconsenthub.application_form.ApplicationForm
import com.betriebsratkanzlei.legalconsenthub.form_element.FormElement import com.betriebsratkanzlei.legalconsenthub.form_element.FormElement
@@ -6,16 +6,15 @@ import com.betriebsratkanzlei.legalconsenthub.user.User
import jakarta.persistence.Column import jakarta.persistence.Column
import jakarta.persistence.Entity import jakarta.persistence.Entity
import jakarta.persistence.EntityListeners import jakarta.persistence.EntityListeners
import jakarta.persistence.GeneratedValue; import jakarta.persistence.GeneratedValue
import jakarta.persistence.Id; import jakarta.persistence.Id
import jakarta.persistence.JoinColumn import jakarta.persistence.JoinColumn
import jakarta.persistence.ManyToOne import jakarta.persistence.ManyToOne
import org.springframework.data.annotation.CreatedDate import org.springframework.data.annotation.CreatedDate
import org.springframework.data.annotation.LastModifiedDate import org.springframework.data.annotation.LastModifiedDate
import org.springframework.data.jpa.domain.support.AuditingEntityListener import org.springframework.data.jpa.domain.support.AuditingEntityListener
import java.time.LocalDateTime import java.time.LocalDateTime
import java.util.UUID
import java.util.UUID;
@Entity @Entity
@EntityListeners(AuditingEntityListener::class) @EntityListeners(AuditingEntityListener::class)
@@ -23,26 +22,20 @@ class Comment(
@Id @Id
@GeneratedValue @GeneratedValue
var id: UUID? = null, var id: UUID? = null,
@Column(nullable = false) @Column(nullable = false)
var message: String = "", var message: String = "",
@ManyToOne @ManyToOne
@JoinColumn(name = "created_by_id", nullable = false) @JoinColumn(name = "created_by_id", nullable = false)
var createdBy: User, var createdBy: User,
@CreatedDate @CreatedDate
@Column(nullable = false) @Column(nullable = false)
var createdAt: LocalDateTime? = null, var createdAt: LocalDateTime? = null,
@LastModifiedDate @LastModifiedDate
@Column(nullable = false) @Column(nullable = false)
var modifiedAt: LocalDateTime? = null, var modifiedAt: LocalDateTime? = null,
@ManyToOne @ManyToOne
@JoinColumn(name = "application_form_id", nullable = false) @JoinColumn(name = "application_form_id", nullable = false)
var applicationForm: ApplicationForm? = null, var applicationForm: ApplicationForm? = null,
@ManyToOne @ManyToOne
@JoinColumn(name = "form_element_id", nullable = false) @JoinColumn(name = "form_element_id", nullable = false)
var formElement: FormElement? = null, var formElement: FormElement? = null,

View File

@@ -11,40 +11,50 @@ import java.util.UUID
@RestController @RestController
class CommentController( class CommentController(
val commentService: CommentService, val commentMapper: CommentMapper, val pagedCommentMapper: PagedCommentMapper val commentService: CommentService,
val commentMapper: CommentMapper,
val pagedCommentMapper: PagedCommentMapper,
) : CommentApi { ) : CommentApi {
@PreAuthorize("hasAnyRole('CHIEF_EXECUTIVE_OFFICER', 'BUSINESS_DEPARTMENT', 'IT_DEPARTMENT', 'HUMAN_RESOURCES', 'HEAD_OF_WORKS_COUNCIL', 'WORKS_COUNCIL', 'EMPLOYEE')") @PreAuthorize(
"hasAnyRole('CHIEF_EXECUTIVE_OFFICER', 'BUSINESS_DEPARTMENT', 'IT_DEPARTMENT', 'HUMAN_RESOURCES', 'HEAD_OF_WORKS_COUNCIL', 'WORKS_COUNCIL', 'EMPLOYEE')",
)
override fun createComment( override fun createComment(
applicationFormId: UUID, applicationFormId: UUID,
formElementId: UUID, formElementId: UUID,
createCommentDto: CreateCommentDto createCommentDto: CreateCommentDto,
): ResponseEntity<CommentDto> { ): ResponseEntity<CommentDto> =
return ResponseEntity.ok( ResponseEntity.ok(
commentMapper.toCommentDto( commentMapper.toCommentDto(
commentService.createComment(applicationFormId, formElementId, createCommentDto) commentService.createComment(applicationFormId, formElementId, createCommentDto),
),
) )
)
}
@PreAuthorize("hasAnyRole('CHIEF_EXECUTIVE_OFFICER', 'BUSINESS_DEPARTMENT', 'IT_DEPARTMENT', 'HUMAN_RESOURCES', 'HEAD_OF_WORKS_COUNCIL', 'WORKS_COUNCIL', 'EMPLOYEE')") @PreAuthorize(
override fun getCommentsByApplicationFormId(applicationFormId: UUID): ResponseEntity<PagedCommentDto> { "hasAnyRole('CHIEF_EXECUTIVE_OFFICER', 'BUSINESS_DEPARTMENT', 'IT_DEPARTMENT', 'HUMAN_RESOURCES', 'HEAD_OF_WORKS_COUNCIL', 'WORKS_COUNCIL', 'EMPLOYEE')",
return ResponseEntity.ok( )
override fun getCommentsByApplicationFormId(applicationFormId: UUID): ResponseEntity<PagedCommentDto> =
ResponseEntity.ok(
pagedCommentMapper.toPagedCommentDto( pagedCommentMapper.toPagedCommentDto(
commentService.getComments(applicationFormId) commentService.getComments(applicationFormId),
),
) )
)
}
@PreAuthorize("hasAnyRole('CHIEF_EXECUTIVE_OFFICER', 'BUSINESS_DEPARTMENT', 'IT_DEPARTMENT', 'HUMAN_RESOURCES', 'HEAD_OF_WORKS_COUNCIL', 'WORKS_COUNCIL', 'EMPLOYEE')") @PreAuthorize(
override fun updateComment(id: UUID, commentDto: CommentDto): ResponseEntity<CommentDto> { "hasAnyRole('CHIEF_EXECUTIVE_OFFICER', 'BUSINESS_DEPARTMENT', 'IT_DEPARTMENT', 'HUMAN_RESOURCES', 'HEAD_OF_WORKS_COUNCIL', 'WORKS_COUNCIL', 'EMPLOYEE')",
return ResponseEntity.ok( )
override fun updateComment(
id: UUID,
commentDto: CommentDto,
): ResponseEntity<CommentDto> =
ResponseEntity.ok(
commentMapper.toCommentDto( commentMapper.toCommentDto(
commentService.updateComment(commentDto) commentService.updateComment(commentDto),
),
) )
)
}
@PreAuthorize("hasAnyRole('CHIEF_EXECUTIVE_OFFICER', 'BUSINESS_DEPARTMENT', 'IT_DEPARTMENT', 'HUMAN_RESOURCES', 'HEAD_OF_WORKS_COUNCIL', 'WORKS_COUNCIL')") @PreAuthorize(
"hasAnyRole('CHIEF_EXECUTIVE_OFFICER', 'BUSINESS_DEPARTMENT', 'IT_DEPARTMENT', 'HUMAN_RESOURCES', 'HEAD_OF_WORKS_COUNCIL', 'WORKS_COUNCIL')",
)
override fun deleteComment(id: UUID): ResponseEntity<Unit> { override fun deleteComment(id: UUID): ResponseEntity<Unit> {
commentService.deleteCommentByID(id) commentService.deleteCommentByID(id)
return ResponseEntity.noContent().build() return ResponseEntity.noContent().build()

View File

@@ -17,25 +17,32 @@ class CommentMapper(
private val userMapper: UserMapper, private val userMapper: UserMapper,
private val userService: UserService, private val userService: UserService,
private val applicationFormRepository: ApplicationFormRepository, private val applicationFormRepository: ApplicationFormRepository,
private val formElementRepository: FormElementRepository private val formElementRepository: FormElementRepository,
) { ) {
fun toCommentDto(comment: Comment): CommentDto { fun toCommentDto(comment: Comment): CommentDto =
return CommentDto( CommentDto(
id = comment.id ?: throw IllegalStateException("Comment ID must not be null!"), id = comment.id ?: throw IllegalStateException("Comment ID must not be null!"),
message = comment.message, message = comment.message,
createdAt = comment.createdAt ?: LocalDateTime.now(), createdAt = comment.createdAt ?: LocalDateTime.now(),
modifiedAt = comment.modifiedAt ?: LocalDateTime.now(), modifiedAt = comment.modifiedAt ?: LocalDateTime.now(),
createdBy = userMapper.toUserDto(comment.createdBy), createdBy = userMapper.toUserDto(comment.createdBy),
applicationFormId = comment.applicationForm?.id applicationFormId =
comment.applicationForm?.id
?: throw IllegalStateException("ApplicationForm ID must not be null!"), ?: throw IllegalStateException("ApplicationForm ID must not be null!"),
formElementId = comment.formElement?.id ?: throw IllegalStateException("FormElement ID must not be null!"), formElementId =
comment.formElement?.id ?: throw IllegalStateException(
"FormElement ID must not be null!",
),
) )
}
fun toComment(commentDto: CommentDto): Comment { fun toComment(commentDto: CommentDto): Comment {
val applicationForm = applicationFormRepository.findById(commentDto.applicationFormId) val applicationForm =
applicationFormRepository
.findById(commentDto.applicationFormId)
.orElseThrow { ApplicationFormNotFoundException(commentDto.applicationFormId) } .orElseThrow { ApplicationFormNotFoundException(commentDto.applicationFormId) }
val formElement = formElementRepository.findById(commentDto.formElementId) val formElement =
formElementRepository
.findById(commentDto.formElementId)
.orElseThrow { FormElementNotFoundException(commentDto.formElementId) } .orElseThrow { FormElementNotFoundException(commentDto.formElementId) }
return Comment( return Comment(
@@ -45,14 +52,22 @@ class CommentMapper(
modifiedAt = commentDto.modifiedAt, modifiedAt = commentDto.modifiedAt,
createdBy = userMapper.toUser(commentDto.createdBy), createdBy = userMapper.toUser(commentDto.createdBy),
applicationForm = applicationForm, applicationForm = applicationForm,
formElement = formElement formElement = formElement,
) )
} }
fun toComment(applicationFormId: UUID, formElementId: UUID, commentDto: CreateCommentDto): Comment { fun toComment(
val applicationForm = applicationFormRepository.findById(applicationFormId) applicationFormId: UUID,
formElementId: UUID,
commentDto: CreateCommentDto,
): Comment {
val applicationForm =
applicationFormRepository
.findById(applicationFormId)
.orElseThrow { FormElementNotFoundException(applicationFormId) } .orElseThrow { FormElementNotFoundException(applicationFormId) }
val formElement = formElementRepository.findById(formElementId) val formElement =
formElementRepository
.findById(formElementId)
.orElseThrow { FormElementNotFoundException(formElementId) } .orElseThrow { FormElementNotFoundException(formElementId) }
val currentUser = userService.getCurrentUser() val currentUser = userService.getCurrentUser()
@@ -61,7 +76,7 @@ class CommentMapper(
message = commentDto.message, message = commentDto.message,
createdBy = currentUser, createdBy = currentUser,
applicationForm = applicationForm, applicationForm = applicationForm,
formElement = formElement formElement = formElement,
) )
} }
} }

View File

@@ -9,5 +9,8 @@ import java.util.UUID
@Repository @Repository
interface CommentRepository : JpaRepository<Comment, UUID> { interface CommentRepository : JpaRepository<Comment, UUID> {
fun findAllByApplicationForm(applicationForm: ApplicationForm, pageable: Pageable): Page<Comment> fun findAllByApplicationForm(
applicationForm: ApplicationForm,
pageable: Pageable,
): Page<Comment>
} }

View File

@@ -5,8 +5,6 @@ import com.betriebsratkanzlei.legalconsenthub.error.CommentNotCreatedException
import com.betriebsratkanzlei.legalconsenthub.error.CommentNotDeletedException import com.betriebsratkanzlei.legalconsenthub.error.CommentNotDeletedException
import com.betriebsratkanzlei.legalconsenthub.error.CommentNotFoundException import com.betriebsratkanzlei.legalconsenthub.error.CommentNotFoundException
import com.betriebsratkanzlei.legalconsenthub.error.CommentNotUpdatedException import com.betriebsratkanzlei.legalconsenthub.error.CommentNotUpdatedException
import com.betriebsratkanzlei.legalconsenthub.error.FormElementNotFoundException
import com.betriebsratkanzlei.legalconsenthub.form_element.FormElementRepository
import com.betriebsratkanzlei.legalconsenthub_api.model.CommentDto import com.betriebsratkanzlei.legalconsenthub_api.model.CommentDto
import com.betriebsratkanzlei.legalconsenthub_api.model.CreateCommentDto import com.betriebsratkanzlei.legalconsenthub_api.model.CreateCommentDto
import org.springframework.data.domain.Page import org.springframework.data.domain.Page
@@ -18,10 +16,13 @@ import java.util.UUID
class CommentService( class CommentService(
private val commentRepository: CommentRepository, private val commentRepository: CommentRepository,
private val applicationFormRepository: ApplicationFormRepository, private val applicationFormRepository: ApplicationFormRepository,
private val commentMapper: CommentMapper private val commentMapper: CommentMapper,
) { ) {
fun createComment(
fun createComment(applicationFormId: UUID, formElementId: UUID, createCommentDto: CreateCommentDto): Comment { applicationFormId: UUID,
formElementId: UUID,
createCommentDto: CreateCommentDto,
): Comment {
val comment = commentMapper.toComment(applicationFormId, formElementId, createCommentDto) val comment = commentMapper.toComment(applicationFormId, formElementId, createCommentDto)
val savedComment: Comment val savedComment: Comment
try { try {
@@ -33,9 +34,7 @@ class CommentService(
return savedComment return savedComment
} }
fun getCommentById(id: UUID): Comment { fun getCommentById(id: UUID): Comment = commentRepository.findById(id).orElseThrow { CommentNotFoundException(id) }
return commentRepository.findById(id).orElseThrow { CommentNotFoundException(id) }
}
fun getComments(applicationFormId: UUID): Page<Comment> { fun getComments(applicationFormId: UUID): Page<Comment> {
val applicationForm = val applicationForm =

View File

@@ -5,9 +5,11 @@ import org.springframework.data.domain.Page
import org.springframework.stereotype.Component import org.springframework.stereotype.Component
@Component @Component
class PagedCommentMapper(private val commentMapper: CommentMapper) { class PagedCommentMapper(
fun toPagedCommentDto(pagedComment: Page<Comment>): PagedCommentDto { private val commentMapper: CommentMapper,
return PagedCommentDto( ) {
fun toPagedCommentDto(pagedComment: Page<Comment>): PagedCommentDto =
PagedCommentDto(
content = pagedComment.content.map { commentMapper.toCommentDto(it) }, content = pagedComment.content.map { commentMapper.toCommentDto(it) },
number = pagedComment.number, number = pagedComment.number,
propertySize = pagedComment.size, propertySize = pagedComment.size,
@@ -18,5 +20,4 @@ class PagedCommentMapper(private val commentMapper: CommentMapper) {
empty = pagedComment.isEmpty, empty = pagedComment.isEmpty,
first = pagedComment.isFirst, first = pagedComment.isFirst,
) )
}
} }

View File

@@ -13,11 +13,10 @@ import org.springframework.security.web.SecurityFilterChain
@EnableWebSecurity @EnableWebSecurity
@EnableMethodSecurity @EnableMethodSecurity
class SecurityConfig { class SecurityConfig {
@Bean @Bean
fun configure( fun configure(
http: HttpSecurity, http: HttpSecurity,
customJwtAuthenticationConverter: CustomJwtAuthenticationConverter customJwtAuthenticationConverter: CustomJwtAuthenticationConverter,
): SecurityFilterChain { ): SecurityFilterChain {
http { http {
csrf { disable() } csrf { disable() }

View File

@@ -8,17 +8,16 @@ import org.springframework.boot.testcontainers.service.connection.ServiceConnect
import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Profile import org.springframework.context.annotation.Profile
import org.testcontainers.containers.PostgreSQLContainer; import org.testcontainers.containers.PostgreSQLContainer
import org.testcontainers.utility.DockerImageName import org.testcontainers.utility.DockerImageName
@Configuration @Configuration
@Profile("testcontainers") @Profile("testcontainers")
class TestContainersConfig { class TestContainersConfig {
@Bean @Bean
@ServiceConnection @ServiceConnection
fun postgresContainer(): PostgreSQLContainer<*> { fun postgresContainer(): PostgreSQLContainer<*> =
return PostgreSQLContainer(DockerImageName.parse("postgres:17-alpine")) PostgreSQLContainer(DockerImageName.parse("postgres:17-alpine"))
.withDatabaseName("legalconsenthub") .withDatabaseName("legalconsenthub")
.withUsername("legalconsenthub") .withUsername("legalconsenthub")
.withPassword("legalconsenthub") .withPassword("legalconsenthub")
@@ -30,12 +29,10 @@ class TestContainersConfig {
this.withPortBindings( this.withPortBindings(
PortBinding( PortBinding(
Ports.Binding.bindPort(5432), Ports.Binding.bindPort(5432),
ExposedPort(5432) ExposedPort(5432),
),
) )
},
) )
} }.withReuse(true)
)
}
.withReuse(true)
}
} }

View File

@@ -7,5 +7,7 @@ class ApplicationFormInvalidStateException(
val applicationFormId: UUID, val applicationFormId: UUID,
val currentState: ApplicationFormStatus, val currentState: ApplicationFormStatus,
val expectedState: ApplicationFormStatus, val expectedState: ApplicationFormStatus,
val operation: String val operation: String,
) : RuntimeException("Cannot $operation application form with ID $applicationFormId. Current state: $currentState, expected state: $expectedState") ) : RuntimeException(
"Cannot $operation application form with ID $applicationFormId. Current state: $currentState, expected state: $expectedState",
)

View File

@@ -1,3 +1,5 @@
package com.betriebsratkanzlei.legalconsenthub.error package com.betriebsratkanzlei.legalconsenthub.error
class ApplicationFormNotCreatedException(e: Exception): RuntimeException("Couldn't create application form", e) class ApplicationFormNotCreatedException(
e: Exception,
) : RuntimeException("Couldn't create application form", e)

View File

@@ -1,3 +1,5 @@
package com.betriebsratkanzlei.legalconsenthub.error package com.betriebsratkanzlei.legalconsenthub.error
class ApplicationFormNotDeletedException(e: Exception): RuntimeException("Couldn't delete application form", e) class ApplicationFormNotDeletedException(
e: Exception,
) : RuntimeException("Couldn't delete application form", e)

View File

@@ -2,4 +2,6 @@ package com.betriebsratkanzlei.legalconsenthub.error
import java.util.UUID import java.util.UUID
class ApplicationFormNotFoundException(id: UUID): RuntimeException("Couldn't find application form with ID: $id") class ApplicationFormNotFoundException(
id: UUID,
) : RuntimeException("Couldn't find application form with ID: $id")

View File

@@ -2,4 +2,7 @@ package com.betriebsratkanzlei.legalconsenthub.error
import java.util.UUID import java.util.UUID
class ApplicationFormNotUpdatedException(e: Exception, id: UUID): RuntimeException("Couldn't update application form with ID: $id", e) class ApplicationFormNotUpdatedException(
e: Exception,
id: UUID,
) : RuntimeException("Couldn't update application form with ID: $id", e)

View File

@@ -1,3 +1,5 @@
package com.betriebsratkanzlei.legalconsenthub.error package com.betriebsratkanzlei.legalconsenthub.error
class CommentNotCreatedException(e: Exception): RuntimeException("Couldn't create comment", e) class CommentNotCreatedException(
e: Exception,
) : RuntimeException("Couldn't create comment", e)

View File

@@ -1,3 +1,5 @@
package com.betriebsratkanzlei.legalconsenthub.error package com.betriebsratkanzlei.legalconsenthub.error
class CommentNotDeletedException(e: Exception): RuntimeException("Couldn't delete comment", e) class CommentNotDeletedException(
e: Exception,
) : RuntimeException("Couldn't delete comment", e)

View File

@@ -2,4 +2,6 @@ package com.betriebsratkanzlei.legalconsenthub.error
import java.util.UUID import java.util.UUID
class CommentNotFoundException(id: UUID): RuntimeException("Couldn't find comment with ID: $id") class CommentNotFoundException(
id: UUID,
) : RuntimeException("Couldn't find comment with ID: $id")

View File

@@ -2,4 +2,7 @@ package com.betriebsratkanzlei.legalconsenthub.error
import java.util.UUID import java.util.UUID
class CommentNotUpdatedException(e: Exception, id: UUID): RuntimeException("Couldn't update comment with ID: $id", e) class CommentNotUpdatedException(
e: Exception,
id: UUID,
) : RuntimeException("Couldn't update comment with ID: $id", e)

View File

@@ -20,14 +20,16 @@ class ExceptionHandler {
@ResponseStatus(HttpStatus.NOT_FOUND) @ResponseStatus(HttpStatus.NOT_FOUND)
fun handleNotFoundError(e: Exception): ResponseEntity<ProblemDetails> { fun handleNotFoundError(e: Exception): ResponseEntity<ProblemDetails> {
logger.warn(e.message, e) logger.warn(e.message, e)
return ResponseEntity.status(HttpStatus.NOT_FOUND).contentType(MediaType.APPLICATION_PROBLEM_JSON) return ResponseEntity
.status(HttpStatus.NOT_FOUND)
.contentType(MediaType.APPLICATION_PROBLEM_JSON)
.body( .body(
ProblemDetails( ProblemDetails(
title = "Not Found", title = "Not Found",
status = HttpStatus.NOT_FOUND.value(), status = HttpStatus.NOT_FOUND.value(),
type = URI.create("about:blank"), type = URI.create("about:blank"),
detail = e.message ?: "Something went wrong" detail = e.message ?: "Something went wrong",
) ),
) )
} }
@@ -36,14 +38,16 @@ class ExceptionHandler {
@ResponseStatus(HttpStatus.BAD_REQUEST) @ResponseStatus(HttpStatus.BAD_REQUEST)
fun handleInvalidStateError(e: ApplicationFormInvalidStateException): ResponseEntity<ProblemDetails> { fun handleInvalidStateError(e: ApplicationFormInvalidStateException): ResponseEntity<ProblemDetails> {
logger.warn(e.message, e) logger.warn(e.message, e)
return ResponseEntity.status(HttpStatus.BAD_REQUEST).contentType(MediaType.APPLICATION_PROBLEM_JSON) return ResponseEntity
.status(HttpStatus.BAD_REQUEST)
.contentType(MediaType.APPLICATION_PROBLEM_JSON)
.body( .body(
ProblemDetails( ProblemDetails(
title = "Invalid State", title = "Invalid State",
status = HttpStatus.BAD_REQUEST.value(), status = HttpStatus.BAD_REQUEST.value(),
type = URI.create("about:blank"), type = URI.create("about:blank"),
detail = e.message ?: "Operation not allowed in current state" detail = e.message ?: "Operation not allowed in current state",
) ),
) )
} }
@@ -52,14 +56,16 @@ class ExceptionHandler {
@ResponseStatus(HttpStatus.CONFLICT) @ResponseStatus(HttpStatus.CONFLICT)
fun handleUserAlreadyExistsError(e: UserAlreadyExistsException): ResponseEntity<ProblemDetails> { fun handleUserAlreadyExistsError(e: UserAlreadyExistsException): ResponseEntity<ProblemDetails> {
logger.warn(e.message, e) logger.warn(e.message, e)
return ResponseEntity.status(HttpStatus.CONFLICT).contentType(MediaType.APPLICATION_PROBLEM_JSON) return ResponseEntity
.status(HttpStatus.CONFLICT)
.contentType(MediaType.APPLICATION_PROBLEM_JSON)
.body( .body(
ProblemDetails( ProblemDetails(
title = "Conflict", title = "Conflict",
status = HttpStatus.CONFLICT.value(), status = HttpStatus.CONFLICT.value(),
type = URI.create("about:blank"), type = URI.create("about:blank"),
detail = e.message ?: "Resource already exists" detail = e.message ?: "Resource already exists",
) ),
) )
} }
@@ -72,14 +78,16 @@ class ExceptionHandler {
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
fun handleInternalServerError(e: Exception): ResponseEntity<ProblemDetails> { fun handleInternalServerError(e: Exception): ResponseEntity<ProblemDetails> {
logger.warn(e.message, e) logger.warn(e.message, e)
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).contentType(MediaType.APPLICATION_PROBLEM_JSON) return ResponseEntity
.status(HttpStatus.INTERNAL_SERVER_ERROR)
.contentType(MediaType.APPLICATION_PROBLEM_JSON)
.body( .body(
ProblemDetails( ProblemDetails(
title = "Internal Server Error", title = "Internal Server Error",
status = HttpStatus.INTERNAL_SERVER_ERROR.value(), status = HttpStatus.INTERNAL_SERVER_ERROR.value(),
type = URI.create("about:blank"), type = URI.create("about:blank"),
detail = e.message ?: "Something went wrong" detail = e.message ?: "Something went wrong",
) ),
) )
} }
} }

View File

@@ -2,4 +2,6 @@ package com.betriebsratkanzlei.legalconsenthub.error
import java.util.UUID import java.util.UUID
class FormElementNotFoundException(id: UUID): RuntimeException("Couldn't find form element with ID: $id") class FormElementNotFoundException(
id: UUID,
) : RuntimeException("Couldn't find form element with ID: $id")

View File

@@ -2,4 +2,6 @@ package com.betriebsratkanzlei.legalconsenthub.error
import java.util.UUID import java.util.UUID
class FormElementSectionNotFoundException(id: UUID): RuntimeException("Couldn't find form element section with ID: $id") class FormElementSectionNotFoundException(
id: UUID,
) : RuntimeException("Couldn't find form element section with ID: $id")

View File

@@ -1,3 +1,5 @@
package com.betriebsratkanzlei.legalconsenthub.error package com.betriebsratkanzlei.legalconsenthub.error
class UserAlreadyExistsException(id: String): RuntimeException("User with ID $id already exists") class UserAlreadyExistsException(
id: String,
) : RuntimeException("User with ID $id already exists")

View File

@@ -1,3 +1,5 @@
package com.betriebsratkanzlei.legalconsenthub.error package com.betriebsratkanzlei.legalconsenthub.error
class UserNotFoundException(id: String): RuntimeException("Couldn't find user with ID: $id") class UserNotFoundException(
id: String,
) : RuntimeException("Couldn't find user with ID: $id")

View File

@@ -1,37 +1,29 @@
package com.betriebsratkanzlei.legalconsenthub.form_element; package com.betriebsratkanzlei.legalconsenthub.form_element
import com.betriebsratkanzlei.legalconsenthub.application_form.ApplicationForm
import com.betriebsratkanzlei.legalconsenthub_api.model.FormElementType import com.betriebsratkanzlei.legalconsenthub_api.model.FormElementType
import jakarta.persistence.CollectionTable import jakarta.persistence.CollectionTable
import jakarta.persistence.Column import jakarta.persistence.Column
import jakarta.persistence.ElementCollection import jakarta.persistence.ElementCollection
import jakarta.persistence.Embeddable
import jakarta.persistence.Entity import jakarta.persistence.Entity
import jakarta.persistence.GeneratedValue; import jakarta.persistence.GeneratedValue
import jakarta.persistence.Id; import jakarta.persistence.Id
import jakarta.persistence.JoinColumn import jakarta.persistence.JoinColumn
import jakarta.persistence.ManyToOne import jakarta.persistence.ManyToOne
import java.util.UUID
import java.util.UUID;
@Entity @Entity
class FormElement( class FormElement(
@Id @Id
@GeneratedValue @GeneratedValue
var id: UUID? = null, var id: UUID? = null,
var title: String? = null, var title: String? = null,
var description: String? = null, var description: String? = null,
@ElementCollection @ElementCollection
@CollectionTable(name = "form_element_options", joinColumns = [JoinColumn(name = "form_element_id")]) @CollectionTable(name = "form_element_options", joinColumns = [JoinColumn(name = "form_element_id")])
var options: MutableList<FormOption> = mutableListOf(), var options: MutableList<FormOption> = mutableListOf(),
@Column(nullable = false) @Column(nullable = false)
var type: FormElementType, var type: FormElementType,
@ManyToOne @ManyToOne
@JoinColumn(name = "form_element_section_id", nullable = false) @JoinColumn(name = "form_element_section_id", nullable = false)
var formElementSection: FormElementSection? = null var formElementSection: FormElementSection? = null,
) )

View File

@@ -8,22 +8,24 @@ import org.springframework.stereotype.Component
@Component @Component
class FormElementMapper( class FormElementMapper(
private val formOptionMapper: FormOptionMapper, private val formOptionMapper: FormOptionMapper,
private val formElementSectionRepository: FormElementSectionRepository private val formElementSectionRepository: FormElementSectionRepository,
) { ) {
fun toFormElementDto(formElement: FormElement): FormElementDto { fun toFormElementDto(formElement: FormElement): FormElementDto =
return FormElementDto( FormElementDto(
id = formElement.id ?: throw IllegalStateException("ApplicationForm ID must not be null!"), id = formElement.id ?: throw IllegalStateException("ApplicationForm ID must not be null!"),
title = formElement.title, title = formElement.title,
description = formElement.description, description = formElement.description,
options = formElement.options.map { formOptionMapper.toFormOptionDto(it) }, options = formElement.options.map { formOptionMapper.toFormOptionDto(it) },
type = formElement.type, type = formElement.type,
formElementSectionId = formElement.formElementSection?.id formElementSectionId =
?: throw IllegalStateException("FormElementSection ID must not be null!") formElement.formElementSection?.id
?: throw IllegalStateException("FormElementSection ID must not be null!"),
) )
}
fun toFormElement(formElement: FormElementDto): FormElement { fun toFormElement(formElement: FormElementDto): FormElement {
val formElementSection = formElementSectionRepository.findById(formElement.formElementSectionId) val formElementSection =
formElementSectionRepository
.findById(formElement.formElementSectionId)
.orElseThrow { FormElementSectionNotFoundException(formElement.formElementSectionId) } .orElseThrow { FormElementSectionNotFoundException(formElement.formElementSectionId) }
return FormElement( return FormElement(
@@ -32,18 +34,20 @@ class FormElementMapper(
description = formElement.description, description = formElement.description,
options = formElement.options.map { formOptionMapper.toFormOption(it) }.toMutableList(), options = formElement.options.map { formOptionMapper.toFormOption(it) }.toMutableList(),
type = formElement.type, type = formElement.type,
formElementSection = formElementSection formElementSection = formElementSection,
) )
} }
fun toFormElement(formElement: CreateFormElementDto, formElementSection: FormElementSection): FormElement { fun toFormElement(
return FormElement( formElement: CreateFormElementDto,
formElementSection: FormElementSection,
): FormElement =
FormElement(
id = null, id = null,
title = formElement.title, title = formElement.title,
description = formElement.description, description = formElement.description,
options = formElement.options.map { formOptionMapper.toFormOption(it) }.toMutableList(), options = formElement.options.map { formOptionMapper.toFormOption(it) }.toMutableList(),
type = formElement.type, type = formElement.type,
formElementSection = formElementSection formElementSection = formElementSection,
) )
}
} }

View File

@@ -1,8 +1,5 @@
package com.betriebsratkanzlei.legalconsenthub.form_element package com.betriebsratkanzlei.legalconsenthub.form_element
import com.betriebsratkanzlei.legalconsenthub.comment.Comment
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable
import org.springframework.data.jpa.repository.JpaRepository import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.stereotype.Repository import org.springframework.stereotype.Repository
import java.util.UUID import java.util.UUID

View File

@@ -1,35 +1,27 @@
package com.betriebsratkanzlei.legalconsenthub.form_element; package com.betriebsratkanzlei.legalconsenthub.form_element
import com.betriebsratkanzlei.legalconsenthub.application_form.ApplicationForm import com.betriebsratkanzlei.legalconsenthub.application_form.ApplicationForm
import jakarta.persistence.CascadeType import jakarta.persistence.CascadeType
import jakarta.persistence.CollectionTable
import jakarta.persistence.Column import jakarta.persistence.Column
import jakarta.persistence.ElementCollection
import jakarta.persistence.Entity import jakarta.persistence.Entity
import jakarta.persistence.GeneratedValue; import jakarta.persistence.GeneratedValue
import jakarta.persistence.Id; import jakarta.persistence.Id
import jakarta.persistence.JoinColumn import jakarta.persistence.JoinColumn
import jakarta.persistence.ManyToOne import jakarta.persistence.ManyToOne
import jakarta.persistence.OneToMany import jakarta.persistence.OneToMany
import java.util.UUID
import java.util.UUID;
@Entity @Entity
class FormElementSection( class FormElementSection(
@Id @Id
@GeneratedValue @GeneratedValue
var id: UUID? = null, var id: UUID? = null,
@Column(nullable = false) @Column(nullable = false)
var title: String, var title: String,
var shortTitle: String? = null, var shortTitle: String? = null,
var description: String? = null, var description: String? = null,
@OneToMany(mappedBy = "formElementSection", cascade = [CascadeType.ALL], orphanRemoval = true) @OneToMany(mappedBy = "formElementSection", cascade = [CascadeType.ALL], orphanRemoval = true)
var formElements: MutableList<FormElement> = mutableListOf(), var formElements: MutableList<FormElement> = mutableListOf(),
@ManyToOne @ManyToOne
@JoinColumn(name = "application_form_id", nullable = false) @JoinColumn(name = "application_form_id", nullable = false)
var applicationForm: ApplicationForm? = null, var applicationForm: ApplicationForm? = null,

View File

@@ -10,22 +10,24 @@ import org.springframework.stereotype.Component
@Component @Component
class FormElementSectionMapper( class FormElementSectionMapper(
private val formElementMapper: FormElementMapper, private val formElementMapper: FormElementMapper,
private val applicationFormRepository: ApplicationFormRepository private val applicationFormRepository: ApplicationFormRepository,
) { ) {
fun toFormElementSectionDto(formElementSection: FormElementSection): FormElementSectionDto { fun toFormElementSectionDto(formElementSection: FormElementSection): FormElementSectionDto =
return FormElementSectionDto( FormElementSectionDto(
id = formElementSection.id ?: throw IllegalStateException("FormElementSection ID must not be null!"), id = formElementSection.id ?: throw IllegalStateException("FormElementSection ID must not be null!"),
title = formElementSection.title, title = formElementSection.title,
description = formElementSection.description, description = formElementSection.description,
shortTitle = formElementSection.shortTitle, shortTitle = formElementSection.shortTitle,
formElements = formElementSection.formElements.map { formElementMapper.toFormElementDto(it) }, formElements = formElementSection.formElements.map { formElementMapper.toFormElementDto(it) },
applicationFormId = formElementSection.applicationForm?.id applicationFormId =
?: throw IllegalStateException("ApplicationForm ID must not be null!") formElementSection.applicationForm?.id
?: throw IllegalStateException("ApplicationForm ID must not be null!"),
) )
}
fun toFormElementSection(formElementSection: FormElementSectionDto): FormElementSection { fun toFormElementSection(formElementSection: FormElementSectionDto): FormElementSection {
val applicationForm = applicationFormRepository.findById(formElementSection.applicationFormId) val applicationForm =
applicationFormRepository
.findById(formElementSection.applicationFormId)
.orElseThrow { ApplicationFormNotFoundException(formElementSection.applicationFormId) } .orElseThrow { ApplicationFormNotFoundException(formElementSection.applicationFormId) }
return FormElementSection( return FormElementSection(
@@ -34,18 +36,23 @@ class FormElementSectionMapper(
description = formElementSection.description, description = formElementSection.description,
shortTitle = formElementSection.shortTitle, shortTitle = formElementSection.shortTitle,
formElements = formElementSection.formElements.map { formElementMapper.toFormElement(it) }.toMutableList(), formElements = formElementSection.formElements.map { formElementMapper.toFormElement(it) }.toMutableList(),
applicationForm = applicationForm applicationForm = applicationForm,
) )
} }
fun toFormElementSection(createFormElementSection: CreateFormElementSectionDto, applicationForm: ApplicationForm): FormElementSection { fun toFormElementSection(
val formElementSection = FormElementSection( createFormElementSection: CreateFormElementSectionDto,
applicationForm: ApplicationForm,
): FormElementSection {
val formElementSection =
FormElementSection(
title = createFormElementSection.title, title = createFormElementSection.title,
description = createFormElementSection.description, description = createFormElementSection.description,
shortTitle = createFormElementSection.shortTitle, shortTitle = createFormElementSection.shortTitle,
applicationForm = applicationForm applicationForm = applicationForm,
) )
formElementSection.formElements = createFormElementSection.formElements formElementSection.formElements =
createFormElementSection.formElements
.map { formElementMapper.toFormElement(it, formElementSection) } .map { formElementMapper.toFormElement(it, formElementSection) }
.toMutableList() .toMutableList()
return formElementSection return formElementSection

View File

@@ -1,7 +1,7 @@
package com.betriebsratkanzlei.legalconsenthub.form_element; package com.betriebsratkanzlei.legalconsenthub.form_element
import com.betriebsratkanzlei.legalconsenthub_api.model.EmployeeDataCategory import com.betriebsratkanzlei.legalconsenthub_api.model.EmployeeDataCategory
import com.betriebsratkanzlei.legalconsenthub_api.model.ProcessingPurpose; import com.betriebsratkanzlei.legalconsenthub_api.model.ProcessingPurpose
import jakarta.persistence.Column import jakarta.persistence.Column
import jakarta.persistence.Embeddable import jakarta.persistence.Embeddable
@@ -9,13 +9,10 @@ import jakarta.persistence.Embeddable
class FormOption( class FormOption(
@Column(nullable = false, name = "option_value") @Column(nullable = false, name = "option_value")
var value: String, var value: String,
@Column(nullable = false) @Column(nullable = false)
var label: String, var label: String,
@Column(nullable = false) @Column(nullable = false)
var processingPurpose: ProcessingPurpose, var processingPurpose: ProcessingPurpose,
@Column(nullable = false) @Column(nullable = false)
var employeeDataCategory: EmployeeDataCategory var employeeDataCategory: EmployeeDataCategory,
) )

View File

@@ -5,21 +5,19 @@ import org.springframework.stereotype.Component
@Component @Component
class FormOptionMapper { class FormOptionMapper {
fun toFormOptionDto(formOption: FormOption): FormOptionDto { fun toFormOptionDto(formOption: FormOption): FormOptionDto =
return FormOptionDto( FormOptionDto(
value = formOption.value, value = formOption.value,
label = formOption.label, label = formOption.label,
processingPurpose = formOption.processingPurpose, processingPurpose = formOption.processingPurpose,
employeeDataCategory = formOption.employeeDataCategory employeeDataCategory = formOption.employeeDataCategory,
) )
}
fun toFormOption(formOptionDto: FormOptionDto): FormOption { fun toFormOption(formOptionDto: FormOptionDto): FormOption =
return FormOption( FormOption(
value = formOptionDto.value, value = formOptionDto.value,
label = formOptionDto.label, label = formOptionDto.label,
processingPurpose = formOptionDto.processingPurpose, processingPurpose = formOptionDto.processingPurpose,
employeeDataCategory = formOptionDto.employeeDataCategory employeeDataCategory = formOptionDto.employeeDataCategory,
) )
}
} }

View File

@@ -3,14 +3,14 @@ package com.betriebsratkanzlei.legalconsenthub.notification
import com.betriebsratkanzlei.legalconsenthub.user.User import com.betriebsratkanzlei.legalconsenthub.user.User
import com.betriebsratkanzlei.legalconsenthub_api.model.NotificationType import com.betriebsratkanzlei.legalconsenthub_api.model.NotificationType
import jakarta.persistence.Column import jakarta.persistence.Column
import jakarta.persistence.JoinColumn
import jakarta.persistence.ManyToOne
import jakarta.persistence.Entity import jakarta.persistence.Entity
import jakarta.persistence.EntityListeners import jakarta.persistence.EntityListeners
import jakarta.persistence.Enumerated
import jakarta.persistence.EnumType import jakarta.persistence.EnumType
import jakarta.persistence.Enumerated
import jakarta.persistence.GeneratedValue import jakarta.persistence.GeneratedValue
import jakarta.persistence.Id import jakarta.persistence.Id
import jakarta.persistence.JoinColumn
import jakarta.persistence.ManyToOne
import org.springframework.data.annotation.CreatedDate import org.springframework.data.annotation.CreatedDate
import org.springframework.data.jpa.domain.support.AuditingEntityListener import org.springframework.data.jpa.domain.support.AuditingEntityListener
import java.time.LocalDateTime import java.time.LocalDateTime
@@ -22,34 +22,25 @@ class Notification(
@Id @Id
@GeneratedValue @GeneratedValue
var id: UUID? = null, var id: UUID? = null,
@Column(nullable = false) @Column(nullable = false)
var title: String = "", var title: String = "",
@Column(nullable = false, columnDefinition = "TEXT") @Column(nullable = false, columnDefinition = "TEXT")
var message: String = "", var message: String = "",
@Column(nullable = false) @Column(nullable = false)
var clickTarget: String = "", var clickTarget: String = "",
@Column(nullable = false) @Column(nullable = false)
var isRead: Boolean = false, var isRead: Boolean = false,
@ManyToOne @ManyToOne
@JoinColumn(name = "recipient_id", nullable = true) @JoinColumn(name = "recipient_id", nullable = true)
var recipient: User?, var recipient: User?,
@Column(nullable = false) @Column(nullable = false)
var role: String = "", var role: String = "",
@Enumerated(EnumType.STRING) @Enumerated(EnumType.STRING)
@Column(nullable = false) @Column(nullable = false)
var type: NotificationType = NotificationType.INFO, var type: NotificationType = NotificationType.INFO,
@Column(nullable = false) @Column(nullable = false)
var organizationId: String = "", var organizationId: String = "",
@CreatedDate @CreatedDate
@Column(nullable = false) @Column(nullable = false)
var createdAt: LocalDateTime? = null var createdAt: LocalDateTime? = null,
) )

View File

@@ -7,11 +7,10 @@ import org.springframework.stereotype.Component
@Component @Component
class NotificationMapper( class NotificationMapper(
private val userMapper: UserMapper private val userMapper: UserMapper,
) { ) {
fun toNotificationDto(notification: Notification): NotificationDto =
fun toNotificationDto(notification: Notification): NotificationDto { NotificationDto(
return NotificationDto(
id = notification.id!!, id = notification.id!!,
title = notification.title, title = notification.title,
message = notification.message, message = notification.message,
@@ -21,12 +20,11 @@ class NotificationMapper(
role = notification.role, role = notification.role,
type = notification.type, type = notification.type,
organizationId = notification.organizationId, organizationId = notification.organizationId,
createdAt = notification.createdAt!! createdAt = notification.createdAt!!,
) )
}
fun toNotification(createNotificationDto: CreateNotificationDto): Notification { fun toNotification(createNotificationDto: CreateNotificationDto): Notification =
return Notification( Notification(
title = createNotificationDto.title, title = createNotificationDto.title,
message = createNotificationDto.message, message = createNotificationDto.message,
clickTarget = createNotificationDto.clickTarget, clickTarget = createNotificationDto.clickTarget,
@@ -34,7 +32,6 @@ class NotificationMapper(
recipient = createNotificationDto.recipient?.let { userMapper.toUser(it) }, recipient = createNotificationDto.recipient?.let { userMapper.toUser(it) },
role = createNotificationDto.role, role = createNotificationDto.role,
type = createNotificationDto.type, type = createNotificationDto.type,
organizationId = createNotificationDto.organizationId organizationId = createNotificationDto.organizationId,
) )
}
} }

View File

@@ -6,11 +6,10 @@ import org.springframework.stereotype.Component
@Component @Component
class PagedNotificationMapper( class PagedNotificationMapper(
private val notificationMapper: NotificationMapper private val notificationMapper: NotificationMapper,
) { ) {
fun toPagedNotificationDto(page: Page<Notification>): PagedNotificationDto =
fun toPagedNotificationDto(page: Page<Notification>): PagedNotificationDto { PagedNotificationDto(
return PagedNotificationDto(
content = page.content.map { notificationMapper.toNotificationDto(it) }, content = page.content.map { notificationMapper.toNotificationDto(it) },
number = page.number, number = page.number,
propertySize = page.size, propertySize = page.size,
@@ -19,7 +18,6 @@ class PagedNotificationMapper(
totalPages = page.totalPages, totalPages = page.totalPages,
first = page.isFirst, first = page.isFirst,
last = page.isLast, last = page.isLast,
empty = page.isEmpty empty = page.isEmpty,
) )
}
} }

View File

@@ -1,17 +1,17 @@
package com.betriebsratkanzlei.legalconsenthub.security package com.betriebsratkanzlei.legalconsenthub.security
import org.springframework.security.core.GrantedAuthority import org.springframework.security.core.GrantedAuthority
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken
import org.springframework.security.oauth2.jwt.Jwt import org.springframework.security.oauth2.jwt.Jwt
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken
class CustomJwtAuthentication( class CustomJwtAuthentication(
jwt: Jwt, jwt: Jwt,
private val principal: CustomJwtTokenPrincipal, private val principal: CustomJwtTokenPrincipal,
authorities: Collection<GrantedAuthority> authorities: Collection<GrantedAuthority>,
) : JwtAuthenticationToken( ) : JwtAuthenticationToken(
jwt, authorities, principal.id jwt,
) { authorities,
override fun getPrincipal(): CustomJwtTokenPrincipal { principal.id,
return principal ) {
} override fun getPrincipal(): CustomJwtTokenPrincipal = principal
} }

View File

@@ -17,7 +17,8 @@ class CustomJwtAuthenticationConverter : Converter<Jwt, AbstractAuthenticationTo
val legalconsenthubResource = resourceAccess?.get("legalconsenthub") as? Map<*, *> val legalconsenthubResource = resourceAccess?.get("legalconsenthub") as? Map<*, *>
val roles = (legalconsenthubResource?.get("roles") as? List<*>)?.mapNotNull { it as? String } ?: emptyList() val roles = (legalconsenthubResource?.get("roles") as? List<*>)?.mapNotNull { it as? String } ?: emptyList()
val authorities: Collection<GrantedAuthority> = roles.map { role -> val authorities: Collection<GrantedAuthority> =
roles.map { role ->
SimpleGrantedAuthority("ROLE_$role") SimpleGrantedAuthority("ROLE_$role")
} }

View File

@@ -14,11 +14,13 @@ import org.springframework.stereotype.Component
import org.springframework.web.filter.OncePerRequestFilter import org.springframework.web.filter.OncePerRequestFilter
@Component @Component
class JwtUserSyncFilter(val userService: UserService) : OncePerRequestFilter() { class JwtUserSyncFilter(
val userService: UserService,
) : OncePerRequestFilter() {
override fun doFilterInternal( override fun doFilterInternal(
request: HttpServletRequest, request: HttpServletRequest,
response: HttpServletResponse, response: HttpServletResponse,
filterChain: FilterChain filterChain: FilterChain,
) { ) {
val logger = LoggerFactory.getLogger(JwtUserSyncFilter::class.java) val logger = LoggerFactory.getLogger(JwtUserSyncFilter::class.java)
val auth: Authentication? = SecurityContextHolder.getContext().authentication val auth: Authentication? = SecurityContextHolder.getContext().authentication
@@ -32,7 +34,8 @@ class JwtUserSyncFilter(val userService: UserService) : OncePerRequestFilter() {
// Extract organization information from JWT // Extract organization information from JWT
val organizationClaim = jwt.getClaimAsMap("organization") val organizationClaim = jwt.getClaimAsMap("organization")
val organizationId = organizationClaim?.values?.firstOrNull()?.let { orgData -> val organizationId =
organizationClaim?.values?.firstOrNull()?.let { orgData ->
if (orgData is Map<*, *>) { if (orgData is Map<*, *>) {
orgData["id"] as? String orgData["id"] as? String
} else { } else {

View File

@@ -1,7 +1,10 @@
package com.betriebsratkanzlei.legalconsenthub.user package com.betriebsratkanzlei.legalconsenthub.user
import com.betriebsratkanzlei.legalconsenthub_api.model.UserStatus import jakarta.persistence.Column
import jakarta.persistence.* import jakarta.persistence.Entity
import jakarta.persistence.EntityListeners
import jakarta.persistence.Id
import jakarta.persistence.Table
import org.springframework.data.annotation.CreatedDate import org.springframework.data.annotation.CreatedDate
import org.springframework.data.annotation.LastModifiedDate import org.springframework.data.annotation.LastModifiedDate
import org.springframework.data.jpa.domain.support.AuditingEntityListener import org.springframework.data.jpa.domain.support.AuditingEntityListener
@@ -14,18 +17,14 @@ class User(
@Id @Id
@Column(nullable = false) @Column(nullable = false)
var keycloakId: String, var keycloakId: String,
@Column(nullable = false) @Column(nullable = false)
var name: String, var name: String,
@Column(nullable = true) @Column(nullable = true)
var organizationId: String? = null, var organizationId: String? = null,
@CreatedDate @CreatedDate
@Column(nullable = false) @Column(nullable = false)
var createdAt: LocalDateTime? = null, var createdAt: LocalDateTime? = null,
@LastModifiedDate @LastModifiedDate
@Column(nullable = false) @Column(nullable = false)
var modifiedAt: LocalDateTime? = null var modifiedAt: LocalDateTime? = null,
) )

View File

@@ -8,7 +8,7 @@ import org.springframework.web.bind.annotation.RestController
@RestController @RestController
class UserController( class UserController(
private val userService: UserService, private val userService: UserService,
private val userMapper: UserMapper private val userMapper: UserMapper,
) : UserApi { ) : UserApi {
override fun getUserById(id: String): ResponseEntity<UserDto> { override fun getUserById(id: String): ResponseEntity<UserDto> {
val user = userService.getUserById(id) val user = userService.getUserById(id)

View File

@@ -5,19 +5,19 @@ import org.springframework.stereotype.Component
@Component @Component
class UserMapper { class UserMapper {
fun toUserDto(user: User): UserDto { fun toUserDto(user: User): UserDto =
return UserDto( UserDto(
keycloakId = user.keycloakId, keycloakId = user.keycloakId,
name = user.name, name = user.name,
organizationId = user.organizationId organizationId = user.organizationId,
) )
}
fun toUser(userDto: UserDto): User { fun toUser(userDto: UserDto): User {
val user = User( val user =
User(
keycloakId = userDto.keycloakId, keycloakId = userDto.keycloakId,
name = userDto.name, name = userDto.name,
organizationId = userDto.organizationId organizationId = userDto.organizationId,
) )
return user return user

View File

@@ -11,14 +11,14 @@ import org.springframework.stereotype.Service
@Service @Service
class UserService( class UserService(
private val userRepository: UserRepository, private val userRepository: UserRepository,
private val userMapper: UserMapper private val userMapper: UserMapper,
) { ) {
fun getCurrentUser(): User { fun getCurrentUser(): User {
val principal = SecurityContextHolder.getContext().authentication.principal as CustomJwtTokenPrincipal val principal = SecurityContextHolder.getContext().authentication.principal as CustomJwtTokenPrincipal
val userId = principal.id ?: throw IllegalStateException("User ID not found") val userId = principal.id ?: throw IllegalStateException("User ID not found")
return userRepository.findById(userId) return userRepository
.findById(userId)
.orElseThrow { UserNotFoundException(userId) } .orElseThrow { UserNotFoundException(userId) }
} }
@@ -42,23 +42,26 @@ class UserService(
throw UserAlreadyExistsException(userDto.keycloakId) throw UserAlreadyExistsException(userDto.keycloakId)
} }
val user = User( val user =
User(
keycloakId = userDto.keycloakId, keycloakId = userDto.keycloakId,
name = userDto.name, name = userDto.name,
organizationId = userDto.organizationId organizationId = userDto.organizationId,
) )
return userRepository.save(user) return userRepository.save(user)
} }
fun getUserById(userId: String): User { fun getUserById(userId: String): User =
return userRepository.findById(userId) userRepository
.findById(userId)
.orElseThrow { UserNotFoundException(userId) } .orElseThrow { UserNotFoundException(userId) }
}
@Transactional @Transactional
fun updateUser(userDto: UserDto): User { fun updateUser(userDto: UserDto): User {
val user = userRepository.findById(userDto.keycloakId) val user =
userRepository
.findById(userDto.keycloakId)
.orElseThrow { UserNotFoundException(userDto.keycloakId) } .orElseThrow { UserNotFoundException(userDto.keycloakId) }
user.name = userDto.name user.name = userDto.name

View File

@@ -5,9 +5,7 @@ import org.springframework.boot.test.context.SpringBootTest
@SpringBootTest @SpringBootTest
class LegalconsenthubApplicationTests { class LegalconsenthubApplicationTests {
@Test @Test
fun contextLoads() { fun contextLoads() {
} }
} }