Move all file in fr.dcproject.
This commit is contained in:
12
src/main/kotlin/fr/dcproject/common/dto/CreatedAt.kt
Normal file
12
src/main/kotlin/fr/dcproject/common/dto/CreatedAt.kt
Normal file
@@ -0,0 +1,12 @@
|
||||
package fr.dcproject.common.dto
|
||||
|
||||
import fr.postgresjson.entity.EntityCreatedAt
|
||||
import org.joda.time.DateTime
|
||||
|
||||
interface CreatedAt {
|
||||
val createdAt: DateTime
|
||||
|
||||
class Imp(parent: EntityCreatedAt) : CreatedAt {
|
||||
override val createdAt: DateTime = parent.createdAt
|
||||
}
|
||||
}
|
||||
14
src/main/kotlin/fr/dcproject/common/dto/Versionable.kt
Normal file
14
src/main/kotlin/fr/dcproject/common/dto/Versionable.kt
Normal file
@@ -0,0 +1,14 @@
|
||||
package fr.dcproject.common.dto
|
||||
|
||||
import fr.postgresjson.entity.EntityVersioning
|
||||
import java.util.UUID
|
||||
|
||||
interface Versionable {
|
||||
val versionId: UUID
|
||||
val versionNumber: Int
|
||||
|
||||
class Imp(parent: EntityVersioning<UUID, Int>) : Versionable {
|
||||
override val versionNumber: Int = parent.versionNumber
|
||||
override val versionId: UUID = parent.versionId
|
||||
}
|
||||
}
|
||||
27
src/main/kotlin/fr/dcproject/common/email/Mailer.kt
Normal file
27
src/main/kotlin/fr/dcproject/common/email/Mailer.kt
Normal file
@@ -0,0 +1,27 @@
|
||||
package fr.dcproject.common.email
|
||||
|
||||
import com.sendgrid.Method
|
||||
import com.sendgrid.Request
|
||||
import com.sendgrid.SendGrid
|
||||
import com.sendgrid.helpers.mail.Mail
|
||||
import java.io.IOException
|
||||
|
||||
class Mailer(
|
||||
private val key: String
|
||||
) {
|
||||
fun sendEmail(action: () -> Mail): Boolean {
|
||||
val mail = action()
|
||||
|
||||
val sg = SendGrid(key)
|
||||
val request = Request()
|
||||
try {
|
||||
request.method = Method.POST
|
||||
request.endpoint = "mail/send"
|
||||
request.body = mail.build()
|
||||
val response = sg.api(request)
|
||||
return response.statusCode == 202
|
||||
} catch (ex: IOException) {
|
||||
throw ex
|
||||
}
|
||||
}
|
||||
}
|
||||
14
src/main/kotlin/fr/dcproject/common/entity/CreatedBy.kt
Normal file
14
src/main/kotlin/fr/dcproject/common/entity/CreatedBy.kt
Normal file
@@ -0,0 +1,14 @@
|
||||
package fr.dcproject.common.entity
|
||||
|
||||
import fr.dcproject.component.citizen.CitizenI
|
||||
import fr.postgresjson.entity.EntityCreatedBy
|
||||
import fr.postgresjson.entity.EntityI
|
||||
|
||||
/**
|
||||
* TODO remove EntityCreatedBy<EntityI>
|
||||
*/
|
||||
interface CreatedBy<T : CitizenI> : EntityCreatedBy<EntityI> {
|
||||
override val createdBy: T
|
||||
}
|
||||
|
||||
class CreatedByImp<T : CitizenI>(override val createdBy: T) : CreatedBy<T>
|
||||
8
src/main/kotlin/fr/dcproject/common/entity/EntityI.kt
Normal file
8
src/main/kotlin/fr/dcproject/common/entity/EntityI.kt
Normal file
@@ -0,0 +1,8 @@
|
||||
package fr.dcproject.common.entity
|
||||
|
||||
import fr.postgresjson.entity.EntityI
|
||||
import java.util.UUID
|
||||
|
||||
interface EntityI : EntityI {
|
||||
val id: UUID
|
||||
}
|
||||
66
src/main/kotlin/fr/dcproject/common/entity/Extra.kt
Normal file
66
src/main/kotlin/fr/dcproject/common/entity/Extra.kt
Normal file
@@ -0,0 +1,66 @@
|
||||
package fr.dcproject.common.entity
|
||||
|
||||
import fr.dcproject.component.article.ArticleRef
|
||||
import fr.dcproject.component.citizen.CitizenI
|
||||
import fr.dcproject.component.comment.generic.CommentRef
|
||||
import fr.dcproject.component.constitution.ConstitutionRef
|
||||
import fr.dcproject.component.opinion.entity.OpinionRef
|
||||
import fr.postgresjson.entity.EntityCreatedAt
|
||||
import fr.postgresjson.entity.EntityCreatedBy
|
||||
import fr.postgresjson.entity.UuidEntity
|
||||
import fr.postgresjson.entity.UuidEntityI
|
||||
import java.util.UUID
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.full.isSubclassOf
|
||||
|
||||
interface ExtraI<T : TargetI, C : CitizenI> :
|
||||
UuidEntityI,
|
||||
HasTarget<T>,
|
||||
EntityCreatedAt,
|
||||
EntityCreatedBy<C>
|
||||
|
||||
interface HasTarget<T : TargetI> {
|
||||
val target: T
|
||||
}
|
||||
|
||||
open class TargetRef(id: UUID? = null, reference: String = "") : TargetI, UuidEntity(id) {
|
||||
|
||||
final override val reference: String
|
||||
get() = if (field != "") field else TargetI.getReference(this)
|
||||
|
||||
init {
|
||||
this.reference = reference
|
||||
}
|
||||
}
|
||||
|
||||
interface TargetI : UuidEntityI {
|
||||
enum class TargetName(val targetReference: String) {
|
||||
Article("article"),
|
||||
Constitution("constitution"),
|
||||
Comment("comment"),
|
||||
Opinion("opinion")
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun <T : TargetI> getReference(t: KClass<T>): String {
|
||||
return when {
|
||||
t.isSubclassOf(ArticleRef::class) -> TargetName.Article.targetReference
|
||||
t.isSubclassOf(ConstitutionRef::class) -> TargetName.Constitution.targetReference
|
||||
t.isSubclassOf(CommentRef::class) -> TargetName.Comment.targetReference
|
||||
t.isSubclassOf(OpinionRef::class) -> TargetName.Opinion.targetReference
|
||||
else -> throw error("target not implemented: ${t.qualifiedName} \nImplement it or return 'reference' from SQL")
|
||||
}
|
||||
}
|
||||
|
||||
fun getReference(t: TargetI): String {
|
||||
val ref = this.getReference(t::class)
|
||||
return if (t is ExtraI<*, *>) {
|
||||
"${ref}_on_${t.target.reference}"
|
||||
} else {
|
||||
ref
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val reference: String
|
||||
}
|
||||
17
src/main/kotlin/fr/dcproject/common/entity/Versionable.kt
Normal file
17
src/main/kotlin/fr/dcproject/common/entity/Versionable.kt
Normal file
@@ -0,0 +1,17 @@
|
||||
package fr.dcproject.common.entity
|
||||
|
||||
import fr.postgresjson.entity.EntityVersioning
|
||||
import java.util.UUID
|
||||
|
||||
interface VersionableRef {
|
||||
val versionId: UUID
|
||||
}
|
||||
|
||||
class VersionableRefImp(
|
||||
override val versionId: UUID
|
||||
) : VersionableRef
|
||||
|
||||
interface Versionable : VersionableRef, EntityVersioning<UUID, Int> {
|
||||
override val versionId: UUID
|
||||
override val versionNumber: Int
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package fr.dcproject.routes
|
||||
|
||||
interface PaginatedRequestI {
|
||||
val page: Int
|
||||
val limit: Int
|
||||
}
|
||||
|
||||
open class PaginatedRequest(
|
||||
page: Int = 1,
|
||||
limit: Int = 50
|
||||
) : PaginatedRequestI {
|
||||
override val page: Int = if (page < 1) 1 else page
|
||||
override val limit: Int = if (limit > 50) 50 else if (limit < 1) 1 else limit
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
package fr.dcproject.common.security
|
||||
|
||||
/** Responses of AccessControl */
|
||||
enum class AccessDecision {
|
||||
GRANTED,
|
||||
DENIED;
|
||||
|
||||
/**
|
||||
* Convert decision to boolean
|
||||
*/
|
||||
fun toBoolean(): Boolean = when (this) {
|
||||
GRANTED -> true
|
||||
DENIED -> false
|
||||
}
|
||||
}
|
||||
|
||||
abstract class AccessControl {
|
||||
/**
|
||||
* A Shortcut for return a GrantedResponse
|
||||
*/
|
||||
protected fun granted(message: String? = null, code: String? = null): GrantedResponse = GrantedResponse(this, message, code)
|
||||
/**
|
||||
* A Shortcut for return a DeniedResponse
|
||||
*/
|
||||
protected fun denied(message: String, code: String): DeniedResponse = DeniedResponse(this, message, code)
|
||||
|
||||
/**
|
||||
* Check all responses and return DENIED if one is DENIED
|
||||
*
|
||||
* If the list of responses is empty, return GRANTED
|
||||
*/
|
||||
private fun AccessResponses.getOneResponse(): AccessResponse = this.firstOrNull { it.decision == AccessDecision.DENIED } ?: granted()
|
||||
|
||||
/**
|
||||
* An helper to convert a list of subject into one response
|
||||
*/
|
||||
protected fun <S : List<T>, T> canAll(items: S, action: (T) -> AccessResponse): AccessResponse = items
|
||||
.map { action(it) }
|
||||
.getOneResponse()
|
||||
}
|
||||
|
||||
/**
|
||||
* Throw an Exception if AccessControl return a DENIED response
|
||||
*/
|
||||
fun <T : AccessControl> T.assert(action: T.() -> AccessResponse) {
|
||||
action().assert()
|
||||
}
|
||||
|
||||
/**
|
||||
* Check all responses and return DENIED if one is DENIED
|
||||
*
|
||||
* If the list of responses is empty, return GRANTED
|
||||
*/
|
||||
fun AccessResponses.getOneResponse(): AccessResponse = this.firstOrNull { it.decision == AccessDecision.DENIED } ?: GrantedResponse(first().accessControl)
|
||||
|
||||
/**
|
||||
* Throw an Exception if one response is DENIED
|
||||
*/
|
||||
fun AccessResponses.assert() = this.getOneResponse().assert()
|
||||
|
||||
class AccessDeniedException(private val accessResponses: AccessResponses) : Throwable(accessResponses.first().message) {
|
||||
constructor(accessResponse: AccessResponse) : this(listOf(accessResponse))
|
||||
|
||||
/**
|
||||
* Get first response
|
||||
*/
|
||||
fun first(): AccessResponse = accessResponses.first()
|
||||
|
||||
/**
|
||||
* Check if the error code is present into the responses
|
||||
*/
|
||||
fun hasErrorCode(code: String): Boolean = accessResponses
|
||||
.filter { it.decision == AccessDecision.DENIED }
|
||||
.any { it.code == code }
|
||||
|
||||
/**
|
||||
* Find and return the response than match with the error code
|
||||
*/
|
||||
fun getErrorCode(code: String): AccessResponse? = accessResponses
|
||||
.firstOrNull { it.decision == AccessDecision.DENIED && it.code == code }
|
||||
|
||||
/**
|
||||
* Get a list of messages of all responses
|
||||
*/
|
||||
fun getMessages(): List<String> = accessResponses
|
||||
.mapNotNull { it.message }
|
||||
|
||||
/**
|
||||
* Get the first message
|
||||
*/
|
||||
fun getFirstMessage(): String? = accessResponses
|
||||
.first()
|
||||
.message
|
||||
}
|
||||
|
||||
/**
|
||||
* The response that all AccessControl method return
|
||||
* @see GrantedResponse
|
||||
* @see DeniedResponse
|
||||
*/
|
||||
sealed class AccessResponse(
|
||||
val decision: AccessDecision,
|
||||
val accessControl: AccessControl,
|
||||
val message: String?,
|
||||
val code: String?
|
||||
) {
|
||||
/**
|
||||
* Convert response as boolean
|
||||
*/
|
||||
fun toBoolean(): Boolean = decision.toBoolean()
|
||||
|
||||
/**
|
||||
* Throw Exception if response if DENIED
|
||||
*/
|
||||
fun assert() {
|
||||
if (this.decision == AccessDecision.DENIED) {
|
||||
throw AccessDeniedException(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class GrantedResponse(
|
||||
accessControl: AccessControl,
|
||||
message: String? = null,
|
||||
code: String? = null
|
||||
) : AccessResponse(AccessDecision.GRANTED, accessControl, message, code)
|
||||
|
||||
class DeniedResponse(
|
||||
accessControl: AccessControl,
|
||||
message: String,
|
||||
code: String
|
||||
) : AccessResponse(AccessDecision.DENIED, accessControl, message, code)
|
||||
|
||||
typealias AccessResponses = List<AccessResponse>
|
||||
6
src/main/kotlin/fr/dcproject/common/utils/DateTime.kt
Normal file
6
src/main/kotlin/fr/dcproject/common/utils/DateTime.kt
Normal file
@@ -0,0 +1,6 @@
|
||||
package fr.dcproject.common.utils
|
||||
|
||||
import org.joda.time.DateTime
|
||||
import org.joda.time.format.ISODateTimeFormat
|
||||
|
||||
fun DateTime.toIso(): String = ISODateTimeFormat.dateTime().print(this)
|
||||
29
src/main/kotlin/fr/dcproject/common/utils/Elastic.kt
Normal file
29
src/main/kotlin/fr/dcproject/common/utils/Elastic.kt
Normal file
@@ -0,0 +1,29 @@
|
||||
package fr.dcproject.common.utils
|
||||
|
||||
import com.jayway.jsonpath.JsonPath
|
||||
import com.jayway.jsonpath.PathNotFoundException
|
||||
import org.apache.http.util.EntityUtils
|
||||
import org.elasticsearch.client.Response
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
fun Response.contentToString(): String {
|
||||
return EntityUtils.toString(this.entity)
|
||||
}
|
||||
|
||||
fun Response.getField(jsonPath: String): Int? {
|
||||
return try {
|
||||
JsonPath.read(this.contentToString(), jsonPath)
|
||||
} catch (e: PathNotFoundException) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun String.getJsonField(jsonPath: String): Int? {
|
||||
return try {
|
||||
JsonPath.read(this, jsonPath)
|
||||
} catch (e: PathNotFoundException) {
|
||||
LoggerFactory.getLogger("fr.dcproject.utils.getJsonField")
|
||||
.warn("No value for Json path ${JsonPath.compile(jsonPath).path}")
|
||||
null
|
||||
}
|
||||
}
|
||||
10
src/main/kotlin/fr/dcproject/common/utils/LoggerDelegate.kt
Normal file
10
src/main/kotlin/fr/dcproject/common/utils/LoggerDelegate.kt
Normal file
@@ -0,0 +1,10 @@
|
||||
package fr.dcproject.common.utils
|
||||
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
import kotlin.properties.ReadOnlyProperty
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
internal class LoggerDelegate<in R : Any> : ReadOnlyProperty<R, Logger> {
|
||||
override fun getValue(thisRef: R, property: KProperty<*>): Logger = LoggerFactory.getLogger(thisRef.javaClass.packageName)
|
||||
}
|
||||
23
src/main/kotlin/fr/dcproject/common/utils/Request.kt
Normal file
23
src/main/kotlin/fr/dcproject/common/utils/Request.kt
Normal file
@@ -0,0 +1,23 @@
|
||||
package fr.dcproject.common.utils
|
||||
|
||||
import com.fasterxml.jackson.module.kotlin.MissingKotlinParameterException
|
||||
import io.ktor.application.ApplicationCall
|
||||
import io.ktor.application.log
|
||||
import io.ktor.features.BadRequestException
|
||||
import io.ktor.request.receive
|
||||
import kotlin.reflect.typeOf
|
||||
|
||||
/**
|
||||
* Receives content for this request.
|
||||
* @param type instance of `KClass` specifying type to be received.
|
||||
* @return instance of [T] received from this call, or `null` if content cannot be transformed to the requested type..
|
||||
*/
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
public suspend inline fun <reified T : Any> ApplicationCall.receiveOrBadRequest(message: String = "Bad Request, wrong body request"): T {
|
||||
return try {
|
||||
receive<T>(typeOf<T>())
|
||||
} catch (cause: MissingKotlinParameterException) {
|
||||
application.log.debug("Conversion failed, throw bad exeption", cause)
|
||||
throw BadRequestException(message, cause)
|
||||
}
|
||||
}
|
||||
7
src/main/kotlin/fr/dcproject/common/utils/Resources.kt
Normal file
7
src/main/kotlin/fr/dcproject/common/utils/Resources.kt
Normal file
@@ -0,0 +1,7 @@
|
||||
package fr.dcproject.common.utils
|
||||
|
||||
fun String.readResource(callback: (String) -> Unit = {}): String {
|
||||
val content = callback::class.java.getResource(this).readText()
|
||||
callback(content)
|
||||
return content
|
||||
}
|
||||
11
src/main/kotlin/fr/dcproject/common/utils/Uuid.kt
Normal file
11
src/main/kotlin/fr/dcproject/common/utils/Uuid.kt
Normal file
@@ -0,0 +1,11 @@
|
||||
package fr.dcproject.common.utils
|
||||
|
||||
import java.util.UUID
|
||||
|
||||
fun String.toUUID(): UUID = UUID.fromString(this.trim())
|
||||
|
||||
fun List<String?>.toUUID(): List<UUID> = this
|
||||
.filterNotNull()
|
||||
.map { it.trim() }
|
||||
.filter { it.isNotBlank() }
|
||||
.map { UUID.fromString(it) }
|
||||
28
src/main/kotlin/fr/dcproject/common/utils/waitElastic.kt
Normal file
28
src/main/kotlin/fr/dcproject/common/utils/waitElastic.kt
Normal file
@@ -0,0 +1,28 @@
|
||||
package fr.dcproject.common.utils
|
||||
|
||||
import org.elasticsearch.client.Request
|
||||
import org.elasticsearch.client.RestClient
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
fun RestClient.waitElasticsearchIsUp() {
|
||||
val logger: Logger = LoggerFactory.getLogger("fr.dcproject.elasticsearch")
|
||||
val request = Request("GET", "/_cluster/health")
|
||||
repeat(5 * 60 / 2) { // 5 minutes
|
||||
runCatching {
|
||||
performRequest(request).statusLine.statusCode
|
||||
}.onSuccess {
|
||||
if (it == 200) {
|
||||
logger.debug("Elasticsearch is Ready! Continue...")
|
||||
return
|
||||
} else {
|
||||
logger.debug("sleep 2s and retry...")
|
||||
Thread.sleep(2000)
|
||||
}
|
||||
}.onFailure {
|
||||
logger.debug("${it.message}, sleep 2s and retry...")
|
||||
Thread.sleep(2000)
|
||||
}
|
||||
}
|
||||
error("Elasticsearch is not ready")
|
||||
}
|
||||
Reference in New Issue
Block a user