Skip to content

Commit

Permalink
Merge pull request #21 from blogify-dev/dev
Browse files Browse the repository at this point in the history
Merge dev into master for PRX4
  • Loading branch information
ranile authored Nov 4, 2019
2 parents 2737f15 + b6d62cc commit 20f6b09
Show file tree
Hide file tree
Showing 112 changed files with 2,203 additions and 461 deletions.
5 changes: 4 additions & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ services:
- '5005:5005'
depends_on:
- db
volumes:
- 'static-data:/var/static/'
db:
image: 'postgres:11.5'
ports:
Expand All @@ -15,4 +17,5 @@ services:
- 'db-data:/var/lib/postgresql/data'

volumes:
db-data:
db-data:
static-data:
13 changes: 9 additions & 4 deletions src/blogify/backend/Application.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@ import blogify.backend.routes.users.users
import blogify.backend.database.Database
import blogify.backend.database.Articles
import blogify.backend.database.Comments
import blogify.backend.database.Uploadables
import blogify.backend.database.Users
import blogify.backend.routes.auth
import blogify.backend.database.handling.query
import blogify.backend.resources.models.Resource
import blogify.backend.routes.static
import blogify.backend.util.SinglePageApplication

import io.ktor.application.call
Expand All @@ -31,18 +33,19 @@ import io.ktor.features.DefaultHeaders
import io.ktor.http.CacheControl
import io.ktor.http.ContentType
import io.ktor.http.content.CachingOptions
import io.ktor.http.content.OutgoingContent
import io.ktor.jackson.jackson
import io.ktor.routing.route
import io.ktor.routing.routing

import org.jetbrains.exposed.sql.SchemaUtils

import kotlinx.coroutines.runBlocking
import org.jetbrains.exposed.sql.update

import org.slf4j.event.Level
import java.util.UUID

const val version = "PRX3"
const val version = "PRX4"

const val asciiLogo = """
__ __ _ ____
Expand Down Expand Up @@ -102,7 +105,7 @@ fun Application.mainModule(@Suppress("UNUSED_PARAMETER") testing: Boolean = fals
// Default headers

install(DefaultHeaders) {
header("Server", "blogify-core PRX3")
header("Server", "blogify-core PRX4")
header("X-Powered-By", "Ktor 1.2.3")
}

Expand Down Expand Up @@ -131,7 +134,8 @@ fun Application.mainModule(@Suppress("UNUSED_PARAMETER") testing: Boolean = fals
Articles,
Articles.Categories,
Users,
Comments
Comments,
Uploadables
)
}}

Expand All @@ -143,6 +147,7 @@ fun Application.mainModule(@Suppress("UNUSED_PARAMETER") testing: Boolean = fals
articles()
users()
auth()
static()
}

get("/") {
Expand Down
4 changes: 4 additions & 0 deletions src/blogify/backend/annotations/BlogifyDsl.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package blogify.backend.annotations

@DslMarker
annotation class BlogifyDsl
11 changes: 11 additions & 0 deletions src/blogify/backend/annotations/check.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package blogify.backend.annotations

import org.intellij.lang.annotations.Language

@Suppress("ClassName")
@Target(AnnotationTarget.TYPE)
@Retention(AnnotationRetention.RUNTIME)
@MustBeDocumented
annotation class check (
@Language(value = "RegExp") val pattern: String
)
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package blogify.backend.util
package blogify.backend.annotations

/**
* Marks a property of a [blogify.backend.resources.models.Resource] to *never* be consumed by [blogify.backend.routes.handling.slice] or [blogify.backend.routes.handling.sanitize]
Expand All @@ -8,4 +8,5 @@ package blogify.backend.util
@Suppress("ClassName")
@Target(AnnotationTarget.PROPERTY)
@Retention(AnnotationRetention.RUNTIME)
annotation class noslice
@MustBeDocumented
annotation class noslice
17 changes: 17 additions & 0 deletions src/blogify/backend/annotations/type.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package blogify.backend.annotations

import org.intellij.lang.annotations.MagicConstant

/**
* Marks a [blogify.backend.resources.static.models.StaticResourceHandle] type as only accepting a
* certain [io.ktor.http.ContentType]. Is queried when a file is uploaded to a handle in a [blogify.backend.resources.models.Resource].
*
* Omitting this annotations is equivalent to setting content type `*`.
*
* @author Benjozork
*/
@Suppress("ClassName")
@Target(AnnotationTarget.TYPE)
@Retention(AnnotationRetention.RUNTIME)
@MustBeDocumented
annotation class type(val contentType: String)
11 changes: 6 additions & 5 deletions src/blogify/backend/auth/handling/Handlers.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ import io.ktor.request.header
import io.ktor.response.respond

import blogify.backend.resources.User
import blogify.backend.routes.handling.CallPipeLineFunction
import blogify.backend.routes.handling.CallPipeline
import blogify.backend.util.BlogifyDsl
import blogify.backend.routes.pipelines.CallPipeLineFunction
import blogify.backend.annotations.BlogifyDsl
import blogify.backend.resources.models.eqr
import blogify.backend.routes.pipelines.CallPipeline
import blogify.backend.util.reason


Expand All @@ -26,7 +27,7 @@ typealias UserAuthPredicate = suspend (user: User) -> Boolean
* @param mustBe the mustBe to whom the token must belong to
*/
fun isUser(mustBe: User): UserAuthPredicate = { user ->
mustBe == user
mustBe eqr user
}

/**
Expand Down Expand Up @@ -63,4 +64,4 @@ suspend fun CallPipeline.runAuthenticated(predicate: UserAuthPredicate, block: C
}
)

}
}
101 changes: 65 additions & 36 deletions src/blogify/backend/database/Tables.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import blogify.backend.resources.Article
import blogify.backend.resources.Comment
import blogify.backend.resources.User
import blogify.backend.resources.models.Resource
import blogify.backend.resources.static.models.StaticResourceHandle
import blogify.backend.services.articles.ArticleService
import blogify.backend.services.UserService
import blogify.backend.services.articles.CommentService
Expand All @@ -14,8 +15,10 @@ import blogify.backend.services.models.Service
import com.github.kittinunf.result.coroutines.SuspendableResult

import io.ktor.application.ApplicationCall
import io.ktor.http.ContentType
import org.jetbrains.exposed.sql.Column

import org.jetbrains.exposed.sql.ReferenceOption
import org.jetbrains.exposed.sql.ReferenceOption.*
import org.jetbrains.exposed.sql.ResultRow
import org.jetbrains.exposed.sql.Table
import org.jetbrains.exposed.sql.select
Expand All @@ -31,26 +34,29 @@ abstract class ResourceTable<R : Resource> : Table() {

object Articles : ResourceTable<Article>() {

val title = varchar ("title", 512)
val title: Column<String> = varchar ("title", 512)
val createdAt = long ("created_at")
val createdBy = uuid ("created_by").references(Users.uuid, onDelete = ReferenceOption.SET_NULL)
val createdBy = uuid ("created_by").references(Users.uuid, onDelete = SET_NULL)
val content = text ("content")
val summary = text ("summary")

override suspend fun convert(callContext: ApplicationCall, source: ResultRow) = SuspendableResult.of<Article, Service.Exception.Fetching> { Article (
uuid = source[uuid],
title = source[title],
createdAt = source[createdAt],
createdBy = UserService.get(callContext, source[createdBy]).get(),
content = source[content],
summary = source[summary],
categories = transaction {
Categories.select { Categories.article eq source[uuid] }.toList() }.map { Categories.convert(it) }
) }
override suspend fun convert(callContext: ApplicationCall, source: ResultRow) = SuspendableResult.of<Article, Service.Exception.Fetching> {
Article (
uuid = source[uuid],
title = source[title],
createdAt = source[createdAt],
createdBy = UserService.get(callContext, source[createdBy]).get(),
content = source[content],
summary = source[summary],
categories = transaction {
Categories.select { Categories.article eq source[uuid] }.toList()
}.map { Categories.convert(it) }
)
}

object Categories : Table() {

val article = uuid("article").primaryKey().references(Articles.uuid, onDelete = ReferenceOption.CASCADE)
val article = uuid("article").primaryKey().references(Articles.uuid, onDelete = CASCADE)
val name = varchar("name", 255).primaryKey()

@Suppress("RedundantSuspendModifier")
Expand All @@ -64,37 +70,60 @@ object Articles : ResourceTable<Article>() {

object Users : ResourceTable<User>() {

val username = varchar ("username", 255)
val password = varchar ("password", 255)
val email = varchar ("email", 255)
val name = varchar ("name", 255)
val username = varchar ("username", 255)
val password = varchar ("password", 255)
val email = varchar ("email", 255)
val name = varchar ("name", 255)
val profilePicture = varchar ("profile_picture", 32).references(Uploadables.fileId, onDelete = SET_NULL, onUpdate = RESTRICT).nullable()

init {
index(true, username)
}

override suspend fun convert(callContext: ApplicationCall, source: ResultRow) = SuspendableResult.of<User, Service.Exception.Fetching> { User (
uuid = source[uuid],
username = source[username],
password = source[password],
name = source[name],
email = source[email]
) }
override suspend fun convert(callContext: ApplicationCall, source: ResultRow) = SuspendableResult.of<User, Service.Exception.Fetching> {
User (
uuid = source[uuid],
username = source[username],
password = source[password],
name = source[name],
email = source[email],
profilePicture = source[profilePicture]?.let { transaction {
Uploadables.select { Uploadables.fileId eq source[profilePicture]!! }.limit(1).single()
}.let { Uploadables.convert(callContext, it).get() } } ?: StaticResourceHandle.None(ContentType.Any)
)
}

}

object Comments : ResourceTable<Comment>() {

val commenter = uuid ("commenter").references(Users.uuid, onDelete = ReferenceOption.SET_NULL)
val article = uuid ("article").references(Articles.uuid, onDelete = ReferenceOption.CASCADE)
val commenter = uuid ("commenter").references(Users.uuid, onDelete = SET_NULL)
val article = uuid ("article").references(Articles.uuid, onDelete = CASCADE)
val content = text ("content")
val parentComment = uuid ("parent_comment").references(uuid, onDelete = ReferenceOption.CASCADE).nullable()

override suspend fun convert(callContext: ApplicationCall, source: ResultRow) = SuspendableResult.of<Comment, Service.Exception.Fetching> { Comment (
uuid = source[uuid],
content = source[content],
article = ArticleService.get(callContext, source[article]).get(),
commenter = UserService.get(callContext, source[commenter]).get(),
parentComment = source[parentComment]?.let { CommentService.get(callContext, it).get() }
) }
val parentComment = uuid ("parent_comment").references(uuid, onDelete = CASCADE).nullable()

override suspend fun convert(callContext: ApplicationCall, source: ResultRow) = SuspendableResult.of<Comment, Service.Exception.Fetching> {
Comment (
uuid = source[uuid],
content = source[content],
article = ArticleService.get(callContext, source[article]).get(),
commenter = UserService.get(callContext, source[commenter]).get(),
parentComment = source[parentComment]?.let { CommentService.get(callContext, it).get() }
)
}

}

object Uploadables : Table() {

val fileId = varchar ("id", 32).primaryKey()
val contentType = varchar ("content_type", 64)

suspend fun convert(callContext: ApplicationCall, source: ResultRow) = SuspendableResult.of<StaticResourceHandle.Ok, Service.Exception> {
StaticResourceHandle.Ok (
contentType = ContentType.parse(source[contentType]),
fileId = source[fileId]
)
}

}
2 changes: 2 additions & 0 deletions src/blogify/backend/database/handling/Handlers.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package blogify.backend.database.handling

import blogify.backend.database.Database
import blogify.backend.annotations.BlogifyDsl

import org.jetbrains.exposed.sql.transactions.experimental.newSuspendedTransaction

Expand All @@ -21,6 +22,7 @@ import java.lang.Exception
*
* @see [Dispatchers.IO]
*/
@BlogifyDsl
suspend fun <T : Any> query(block: suspend () -> T): SuspendableResult<T, Database.Exception> {
return SuspendableResult.of<T, Exception> { // The transaction can throw any Exception; specify that
withContext(Dispatchers.IO) { newSuspendedTransaction { block() } } // Run the transaction
Expand Down
4 changes: 3 additions & 1 deletion src/blogify/backend/resources/Article.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import com.fasterxml.jackson.annotation.JsonIdentityInfo
import com.fasterxml.jackson.annotation.JsonIdentityReference
import com.fasterxml.jackson.annotation.ObjectIdGenerators

import blogify.backend.annotations.check
import blogify.backend.database.Articles
import blogify.backend.resources.models.Resource
import blogify.backend.database.handling.query
Expand All @@ -28,7 +29,8 @@ import java.util.*
property = "uuid"
)
data class Article (
val title: String,
val title: @check("\\w+") String,

val createdAt: Long = Date().time,

@JsonIdentityReference(alwaysAsId = true)
Expand Down
20 changes: 12 additions & 8 deletions src/blogify/backend/resources/User.kt
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package blogify.backend.resources

import blogify.backend.annotations.check
import blogify.backend.resources.models.Resource
import blogify.backend.resources.static.models.StaticResourceHandle
import blogify.backend.annotations.noslice
import blogify.backend.annotations.type

import com.fasterxml.jackson.annotation.JsonIdentityInfo
import com.fasterxml.jackson.annotation.ObjectIdGenerators

import blogify.backend.resources.models.Resource
import blogify.backend.util.noslice

import java.util.*

@JsonIdentityInfo (
Expand All @@ -15,10 +18,11 @@ import java.util.*
property = "uuid"
)
data class User (
val username: String,
@noslice val password: String, // IMPORTANT : DO NOT EVER REMOVE THIS ANNOTATION !
val name: String,
val email: String,
val username: String,
@noslice val password: String, // IMPORTANT : DO NOT EVER REMOVE THIS ANNOTATION !
val name: String,
val email: String,
val profilePicture: @type("image/png") StaticResourceHandle,

override val uuid: UUID = UUID.randomUUID()
) : Resource(uuid)
) : Resource(uuid)
5 changes: 4 additions & 1 deletion src/blogify/backend/resources/models/Resource.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import com.fasterxml.jackson.databind.ser.std.StdSerializer
import blogify.backend.resources.Article
import blogify.backend.resources.Comment
import blogify.backend.resources.User
import blogify.backend.resources.slicing.models.Mapped
import blogify.backend.services.UserService
import blogify.backend.services.articles.ArticleService
import blogify.backend.services.articles.CommentService
Expand All @@ -25,7 +26,7 @@ import kotlinx.coroutines.runBlocking
import java.lang.IllegalStateException
import java.util.*

open class Resource(open val uuid: UUID = UUID.randomUUID()) {
open class Resource(open val uuid: UUID = UUID.randomUUID()) : Mapped() {

object ObjectResolver : ObjectIdResolver {

Expand Down Expand Up @@ -115,3 +116,5 @@ open class Resource(open val uuid: UUID = UUID.randomUUID()) {
}

}

infix fun <T : Resource> T.eqr(other: T) = this.uuid == other.uuid
Loading

0 comments on commit 20f6b09

Please sign in to comment.