Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .codebuddy/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
db/
41 changes: 41 additions & 0 deletions .codebuddy/summary.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Project Summary

## Overview
This project is developed using Kotlin as the primary programming language, and it leverages various frameworks and libraries commonly used in modern software development. The project appears to be a backend service that likely manages products, with functionalities for creating, retrieving, and managing product data. It also includes configurations for Docker and Helm, indicating a focus on containerization and deployment in cloud environments.

### Languages, Frameworks, and Main Libraries Used
- **Language**: Kotlin
- **Build Tool**: Maven (indicated by `pom.xml`)
- **Containerization**: Docker (indicated by multiple Dockerfiles)
- **Documentation**: Markdown files for CI/CD and other related documentation
- **Testing Framework**: Presumably JUnit or similar, as indicated by the test files

## Purpose of the Project
The purpose of the project is to provide a robust backend service that manages product-related operations, including creating and retrieving product information. It likely supports RESTful API endpoints for interaction with clients. The use of Docker and Helm suggests that the project is intended for deployment in cloud environments, facilitating scalability and maintainability.

## Configuration and Build Files
Here are the relevant files for configuration and building the project:

1. `/pom.xml`
2. `/mvnw`
3. `/mvnw.cmd`
4. `/Dockerfile`
5. `/Dockerfile.dev`
6. `/sonar-project.properties`
7. `/helm/Chart.yaml`
8. `/helm/values-dev.yaml`
9. `/helm/values-prod.yaml`
10. `/helm/values.yaml`

## Source Files Location
The source files can be found in the following directory:
- `/src/main/kotlin`

## Documentation Files Location
Documentation files are located in the following directory:
- `/docs`
- Additionally, there are README files located at:
- `/README.md`
- `/helm/README.md`

This project structure indicates a well-organized codebase with clear separation of concerns, making it easier to maintain and extend in the future.
7 changes: 6 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,9 @@ ACCESS_TOKEN_COOKIE_SECURE=
ACCESS_TOKEN_COOKIE_SAME_SITE=
ACCESS_TOKEN_COOKIE_DOMAIN=
ACCESS_TOKEN_COOKIE_PATH=
ACCESS_TOKEN_COOKIE_MAX_AGE=
ACCESS_TOKEN_COOKIE_MAX_AGE=

# PostgreSQL Database Configuration
POSTGRESQL_DB_URL=
POSTGRESQL_DB_USERNAME=
POSTGRESQL_DB_PASSWORD=
Comment on lines +12 to +14

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Check de nieuwste code van Daan, heeft nu namelijk andere env variables. Die heeft er nu namelijk 6 ofzo en die structuur moet je overnemen als je wilt dat je db werkt in openshift. Kijk ook naar zijn application.properties

8 changes: 6 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@
<groupId>io.quarkus</groupId>
<artifactId>quarkus-micrometer-registry-prometheus</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jdbc-postgresql</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5</artifactId>
Expand Down Expand Up @@ -238,8 +242,8 @@
<include>.gitignore</include>
</includes>
<!-- define the steps to apply to those files -->
<trimTrailingWhitespace/>
<endWithNewline/>
<trimTrailingWhitespace />
<endWithNewline />
Comment on lines +245 to +246

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

remove extra space here

<indent>
<tabs>true</tabs>
<spacesPerTab>4</spacesPerTab>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.ptss.support.api.controllers

import jakarta.enterprise.context.ApplicationScoped
import jakarta.ws.rs.GET
import jakarta.ws.rs.Path
import jakarta.ws.rs.Produces
import jakarta.ws.rs.core.MediaType
import org.ptss.support.api.dtos.responses.QuestionnaireResponse
import org.ptss.support.core.facades.QuestionnaireFacade
import org.ptss.support.domain.interfaces.controllers.IQuestionnaireController

@Path("/questionnaires")
@ApplicationScoped
@Produces(MediaType.APPLICATION_JSON)
class QuestionnaireController(
private val questionnaireFacade: QuestionnaireFacade,
) : IQuestionnaireController {

@GET
override suspend fun getAllQuestionnaires(): List<QuestionnaireResponse> =
questionnaireFacade.getAllQuestionnaires()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.ptss.support.api.dtos.responses

data class QuestionnaireResponse(
val id: String,
val title: String,
val description: String,
val estimatedTimeOfCompletion: String,
val assignedAt: String,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

why is assigned at a string? Should be date time no?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

In the swagger it is annotated this way so the response should give back a string right?

@SemPlaatsman SemPlaatsman Dec 24, 2024

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

One question, assignedAt is not confused with assignedTo right? Cause does the assignedAt field mean the date at which the questionnaire was assigned, or who it was assigned to? Because if it's the date at which the questionnaire was assigned, datetime is more logical, but if it's confused with who it was assigned to, then it should be string (and maybe a more clear name, e.g. assignedUserId or something).

PS: Something seems off in Swagger, always double check.

val isFinished: Boolean,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.ptss.support.core.facades

import jakarta.enterprise.context.ApplicationScoped
import jakarta.inject.Inject
import org.ptss.support.api.dtos.responses.QuestionnaireResponse
import org.ptss.support.core.mappers.QuestionnaireMapper
import org.ptss.support.core.services.QuestionnaireService

@ApplicationScoped
class QuestionnaireFacade @Inject constructor(
private val questionnaireService: QuestionnaireService,
) {
suspend fun getAllQuestionnaires(): List<QuestionnaireResponse> =
questionnaireService.getAllQuestionnairesAsync()
.map(QuestionnaireMapper::toResponse)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package org.ptss.support.core.mappers

import org.ptss.support.api.dtos.responses.QuestionnaireResponse
import org.ptss.support.domain.models.Questionnaire

object QuestionnaireMapper {
fun toResponse(questionnaire: Questionnaire): QuestionnaireResponse {
return QuestionnaireResponse(
id = questionnaire.id,
title = questionnaire.title,
description = questionnaire.description,
estimatedTimeOfCompletion = questionnaire.estimatedTimeOfCompletion,
assignedAt = questionnaire.assignedAt,
isFinished = questionnaire.isFinished,
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.ptss.support.core.services

import jakarta.enterprise.context.ApplicationScoped
import org.ptss.support.domain.models.Questionnaire
import org.ptss.support.domain.queries.GetAllQuestionnairesQuery
import org.ptss.support.infrastructure.handlers.queries.questionnaire.GetAllQuestionnairesQueryHandler
import org.ptss.support.infrastructure.util.executeWithExceptionLoggingAsync
import org.slf4j.LoggerFactory

@ApplicationScoped
class QuestionnaireService(
private val getAllQuestionnairesQueryHandler: GetAllQuestionnairesQueryHandler,
) {
private val logger = LoggerFactory.getLogger(QuestionnaireService::class.java)

suspend fun getAllQuestionnairesAsync(): List<Questionnaire> {
return logger.executeWithExceptionLoggingAsync(
operation = { getAllQuestionnairesQueryHandler.handleAsync(GetAllQuestionnairesQuery()) },
logMessage = "Failed to get all questionnaires"
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package org.ptss.support.domain.interfaces.controllers

import jakarta.ws.rs.GET
import org.eclipse.microprofile.openapi.annotations.Operation
import org.eclipse.microprofile.openapi.annotations.media.Content
import org.eclipse.microprofile.openapi.annotations.media.Schema
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse
import org.eclipse.microprofile.openapi.annotations.responses.APIResponses
import org.ptss.support.api.dtos.responses.QuestionnaireResponse
import org.ptss.support.common.exceptions.ServiceError

// error 201, 400, 401, 403 and 500
interface IQuestionnaireController {
@GET
@Operation(summary = "Get all questionnaires", description = "Retrieves a list of all questionnaires")
@APIResponses(
APIResponse(
responseCode = "201",
description = "List of questionnaires successfully retrieved",
content = [Content(schema = Schema(implementation = Array<QuestionnaireResponse>::class))],
),
APIResponse(
responseCode = "400",
description = "Bad request",
content = [Content(schema = Schema(implementation = ServiceError::class))],
),
APIResponse(
responseCode = "401",
description = "Unauthorized",
content = [Content(schema = Schema(implementation = ServiceError::class))],
),
APIResponse(
responseCode = "403",
description = "Forbidden",
content = [Content(schema = Schema(implementation = ServiceError::class))],
),
APIResponse(
responseCode = "500",
description = "Internal server error",
content = [Content(schema = Schema(implementation = ServiceError::class))],
),
)
suspend fun getAllQuestionnaires(): List<QuestionnaireResponse>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package org.ptss.support.domain.interfaces.queries

class GetAllQuestionnairesQuery()
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.ptss.support.domain.interfaces.repositories

import org.ptss.support.domain.models.Questionnaire

interface IQuestionnaireRepository {
fun getAll(): List<Questionnaire>
}
26 changes: 26 additions & 0 deletions src/main/kotlin/org/ptss/support/domain/models/Questionnaire.kt

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

why do you need this to be vars btw? can't you just make this a data class?

data class Questionnaire(
    val id: String,
    val title: String,
    val description: String,
    val estimatedTimeOfCompletion: String,
    val assignedAt: String,
    val isFinished: Boolean
)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

true, changed it

Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package org.ptss.support.domain.models

class Questionnaire() {
var id: String = ""
var title: String = ""
var description: String = ""
var estimatedTimeOfCompletion: String = ""
var assignedAt: String = ""
Comment on lines +4 to +8

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

these default values are redundant, as it automatically becomes an empty string

var isFinished: Boolean = false
Comment thread
CyberAve2100 marked this conversation as resolved.

constructor(
id: String,
title: String,
description: String,
estimatedTimeOfCompletion: String,
assignedAt: String,
isFinished: Boolean
) : this() {
this.id = id
this.title = title
this.description = description
this.estimatedTimeOfCompletion = estimatedTimeOfCompletion
this.assignedAt = assignedAt
this.isFinished = isFinished
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package org.ptss.support.domain.queries

class GetAllQuestionnairesQuery()
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.ptss.support.infrastructure.config

import io.smallrye.config.ConfigMapping
import io.smallrye.config.WithName

@ConfigMapping(prefix = "quarkus.datasource")
interface PostgreSQLConfig {
@WithName("jdbc.url")
fun url(): String

@WithName("username")
fun username(): String

@WithName("password")
fun password(): String
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package org.ptss.support.infrastructure.handlers.queries.questionnaire

import jakarta.enterprise.context.ApplicationScoped
import org.ptss.support.domain.interfaces.queries.IQueryHandler
import org.ptss.support.domain.interfaces.repositories.IQuestionnaireRepository
import org.ptss.support.domain.models.Questionnaire
import org.ptss.support.domain.queries.GetAllQuestionnairesQuery

@ApplicationScoped
class GetAllQuestionnairesQueryHandler(
private val questionnaireRepository: IQuestionnaireRepository,
) : IQueryHandler<GetAllQuestionnairesQuery, List<Questionnaire>> {

override suspend fun handleAsync(query: GetAllQuestionnairesQuery): List<Questionnaire> {
return questionnaireRepository.getAll()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package org.ptss.support.infrastructure.persistence.entities

import org.ptss.support.domain.models.Questionnaire

data class QuestionnaireEntity(
val id: String,
val title: String,
val description: String,
val estimatedTimeOfCompletion: String,
val assignedAt: String,
val isFinished: Boolean,
) {
fun toDomain(): Questionnaire {
return Questionnaire(
id = id,
title = title,
description = description,
estimatedTimeOfCompletion = estimatedTimeOfCompletion,
assignedAt = assignedAt,
isFinished = isFinished,
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package org.ptss.support.infrastructure.repositories

import jakarta.inject.Inject
import org.ptss.support.infrastructure.config.PostgreSQLConfig
import java.sql.Connection
import java.sql.DriverManager

abstract class BaseRepository<T> {
@Inject
protected lateinit var config: PostgreSQLConfig

protected fun getConnection(): Connection {
return DriverManager.getConnection(
config.url(),
config.username(),
config.password(),
)
}

fun <R> useConnection(block: (Connection) -> R): R {
return getConnection().use { conn ->
block(conn)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.ptss.support.infrastructure.repositories

import jakarta.enterprise.context.ApplicationScoped
import org.ptss.support.domain.models.Questionnaire

@ApplicationScoped
class QuestionnaireBaseRepository : BaseRepository<Questionnaire>()
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package org.ptss.support.infrastructure.repositories

import jakarta.enterprise.context.ApplicationScoped
import jakarta.inject.Inject
import org.ptss.support.domain.interfaces.repositories.IQuestionnaireRepository
import org.ptss.support.domain.models.Questionnaire
import org.ptss.support.infrastructure.persistence.entities.QuestionnaireEntity

@ApplicationScoped
class QuestionnaireRepository @Inject constructor(
private val questionnaireBaseRepository: QuestionnaireBaseRepository
) : IQuestionnaireRepository {
override fun getAll(): List<Questionnaire> {
return questionnaireBaseRepository.useConnection { connection ->
val questionnaires = mutableListOf<Questionnaire>()
val query =
"SELECT id, title, description, estimatedTimeOfCompletion, assignedAt, isFinished FROM questionnaires"

val result = connection.prepareStatement(query).executeQuery()

while (result.next()) {
val questionnaire = QuestionnaireEntity(
id = result.getString("id"),
title = result.getString("title"),
description = result.getString("description"),
estimatedTimeOfCompletion = result.getString("estimatedTimeOfCompletion"),
assignedAt = result.getString("assignedAt"),
isFinished = result.getBoolean("isFinished"),
).toDomain()
questionnaires.add(questionnaire)
}

questionnaires
}
}
}
Loading