Small refactor

This commit is contained in:
Hubert Van De Walle 2020-09-29 23:42:03 +02:00
parent 0dfb2a7e03
commit 1bc45461c3
15 changed files with 226 additions and 224 deletions

View File

@ -1,32 +1,23 @@
package be.simplenotes.app
import org.eclipse.jetty.server.Server
import org.eclipse.jetty.server.ServerConnector
import org.http4k.routing.RoutingHttpHandler
import org.http4k.server.ConnectorBuilder
import org.http4k.server.Jetty
import org.http4k.server.ServerConfig
import org.http4k.server.asServer
import org.http4k.server.Http4kServer
import org.slf4j.LoggerFactory
import be.simplenotes.shared.config.ServerConfig as SimpleNotesServeConfig
import be.simplenotes.shared.config.ServerConfig as SimpleNotesServerConfig
class Server(
private val config: SimpleNotesServeConfig,
private val serverConfig: ServerConfig,
private val router: RoutingHttpHandler,
private val config: SimpleNotesServerConfig,
private val http4kServer: Http4kServer,
) {
fun start() {
router.asServer(serverConfig).start()
LoggerFactory.getLogger(javaClass).info("Listening on http://${config.host}:${config.port}")
}
}
private val logger = LoggerFactory.getLogger(javaClass)
fun serverConfig(config: SimpleNotesServeConfig): ServerConfig {
val builder: ConnectorBuilder = { server: Server ->
ServerConnector(server).apply {
port = config.port
host = config.host
}
fun start(): Server {
http4kServer.start()
logger.info("Listening on http://${config.host}:${config.port}")
return this
}
fun stop() {
logger.info("Stopping server")
http4kServer.close()
}
return Jetty(config.port, builder)
}

View File

@ -1,180 +1,25 @@
package be.simplenotes.app
import be.simplenotes.app.api.ApiNoteController
import be.simplenotes.app.api.ApiUserController
import be.simplenotes.app.controllers.BaseController
import be.simplenotes.app.controllers.NoteController
import be.simplenotes.app.controllers.SettingsController
import be.simplenotes.app.controllers.UserController
import be.simplenotes.app.filters.AuthFilter
import be.simplenotes.app.filters.AuthType
import be.simplenotes.app.filters.ErrorFilter
import be.simplenotes.app.filters.JwtSource
import be.simplenotes.app.routes.Router
import be.simplenotes.app.utils.StaticFileResolver
import be.simplenotes.app.utils.StaticFileResolverImpl
import be.simplenotes.app.views.*
import be.simplenotes.app.modules.*
import be.simplenotes.domain.domainModule
import be.simplenotes.domain.usecases.NoteService
import be.simplenotes.persistance.DbMigrations
import be.simplenotes.persistance.persistanceModule
import be.simplenotes.search.searchModule
import be.simplenotes.shared.config.DataSourceConfig
import be.simplenotes.shared.config.JwtConfig
import kotlinx.serialization.KSerializer
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.json.Json
import kotlinx.serialization.modules.SerializersModule
import org.http4k.core.RequestContexts
import org.koin.core.context.startKoin
import org.koin.core.qualifier.named
import org.koin.core.qualifier.qualifier
import org.koin.dsl.module
import org.slf4j.LoggerFactory
import java.time.LocalDateTime
import java.util.*
import be.simplenotes.shared.config.ServerConfig as SimpleNotesServeConfig
fun main() {
val koin = startKoin {
startKoin {
modules(
serverModule,
persistanceModule,
configModule,
domainModule,
serverModule,
userModule,
baseModule,
userModule,
noteModule,
settingsModule,
domainModule,
searchModule,
apiModule,
jsonModule
)
}.koin
val dataSourceConfig = koin.get<DataSourceConfig>()
val jwtConfig = koin.get<JwtConfig>()
val serverConfig = koin.get<SimpleNotesServeConfig>()
val logger = LoggerFactory.getLogger("SimpleNotes")
logger.info("datasource: $dataSourceConfig")
logger.info("jwt: $jwtConfig")
logger.info("server: $serverConfig")
val migrations = koin.get<DbMigrations>()
migrations.migrate()
val noteService = koin.get<NoteService>()
noteService.dropAllIndexes()
noteService.indexAll()
koin.get<Server>().start()
}
val serverModule = module {
single { Server(get(), get(), get()) }
single<StaticFileResolver> { StaticFileResolverImpl() }
single {
Router(
get(),
get(),
get(),
get(),
get(),
get(),
requiredAuth = get(AuthType.Required.qualifier),
optionalAuth = get(AuthType.Optional.qualifier),
errorFilter = get(named("ErrorFilter")),
apiAuth = get(named("apiAuthFilter")),
get()
)()
}
single { serverConfig(get()) }
single { RequestContexts() }
single(AuthType.Optional.qualifier) { AuthFilter(get(), AuthType.Optional, get())() }
single(AuthType.Required.qualifier) { AuthFilter(get(), AuthType.Required, get())() }
single(named("ErrorFilter")) { ErrorFilter(get())() }
single { ErrorView(get()) }
}
val userModule = module {
single { UserController(get(), get(), get()) }
single { UserView(get()) }
}
val baseModule = module {
single { BaseController(get()) }
single { BaseView(get()) }
}
val noteModule = module {
single { NoteController(get(), get()) }
single { NoteView(get()) }
}
val settingsModule = module {
single { SettingsController(get(), get()) }
single { SettingView(get()) }
}
val configModule = module {
single { Config.dataSourceConfig }
single { Config.jwtConfig }
single { Config.serverConfig }
}
val apiModule = module {
single { ApiUserController(get(), get()) }
single { ApiNoteController(get(), get()) }
single {
Json {
prettyPrint = true
serializersModule = get()
}
}
single {
SerializersModule {
contextual(LocalDateTime::class, LocalDateTimeSerializer)
contextual(UUID::class, UuidSerializer)
}
}
single(named("apiAuthFilter")) {
AuthFilter(
extractor = get(),
authType = AuthType.Required,
ctx = get(),
source = JwtSource.Header,
redirect = false
)()
}
}
internal object LocalDateTimeSerializer : KSerializer<LocalDateTime> {
override val descriptor: SerialDescriptor
get() = PrimitiveSerialDescriptor("LocalDateTime", PrimitiveKind.STRING)
override fun serialize(encoder: Encoder, value: LocalDateTime) {
encoder.encodeString(value.toString())
}
override fun deserialize(decoder: Decoder): LocalDateTime {
TODO("Not implemented, isn't needed")
}
}
internal object UuidSerializer : KSerializer<UUID> {
override val descriptor: SerialDescriptor
get() = PrimitiveSerialDescriptor("LocalDateTime", PrimitiveKind.STRING)
override fun serialize(encoder: Encoder, value: UUID) {
encoder.encodeString(value.toString())
}
override fun deserialize(decoder: Decoder): UUID {
TODO()
}
}

View File

@ -0,0 +1,23 @@
package be.simplenotes.app.modules
import be.simplenotes.app.api.ApiNoteController
import be.simplenotes.app.api.ApiUserController
import be.simplenotes.app.filters.AuthFilter
import be.simplenotes.app.filters.AuthType
import be.simplenotes.app.filters.JwtSource
import org.koin.core.qualifier.named
import org.koin.dsl.module
val apiModule = module {
single { ApiUserController(get(), get()) }
single { ApiNoteController(get(), get()) }
single(named("apiAuthFilter")) {
AuthFilter(
extractor = get(),
authType = AuthType.Required,
ctx = get(),
source = JwtSource.Header,
redirect = false
)()
}
}

View File

@ -0,0 +1,10 @@
package be.simplenotes.app.modules
import be.simplenotes.app.Config
import org.koin.dsl.module
val configModule = module {
single { Config.dataSourceConfig }
single { Config.jwtConfig }
single { Config.serverConfig }
}

View File

@ -0,0 +1,31 @@
package be.simplenotes.app.modules
import be.simplenotes.app.controllers.BaseController
import be.simplenotes.app.controllers.NoteController
import be.simplenotes.app.controllers.SettingsController
import be.simplenotes.app.controllers.UserController
import be.simplenotes.app.views.BaseView
import be.simplenotes.app.views.NoteView
import be.simplenotes.app.views.SettingView
import be.simplenotes.app.views.UserView
import org.koin.dsl.module
val userModule = module {
single { UserController(get(), get(), get()) }
single { UserView(get()) }
}
val baseModule = module {
single { BaseController(get()) }
single { BaseView(get()) }
}
val noteModule = module {
single { NoteController(get(), get()) }
single { NoteView(get()) }
}
val settingsModule = module {
single { SettingsController(get(), get()) }
single { SettingView(get()) }
}

View File

@ -0,0 +1,24 @@
package be.simplenotes.app.modules
import be.simplenotes.app.serialization.LocalDateTimeSerializer
import be.simplenotes.app.serialization.UuidSerializer
import kotlinx.serialization.json.Json
import kotlinx.serialization.modules.SerializersModule
import org.koin.dsl.module
import java.time.LocalDateTime
import java.util.*
val jsonModule = module {
single {
Json {
prettyPrint = true
serializersModule = get()
}
}
single {
SerializersModule {
contextual(LocalDateTime::class, LocalDateTimeSerializer())
contextual(UUID::class, UuidSerializer())
}
}
}

View File

@ -0,0 +1,57 @@
package be.simplenotes.app.modules
import be.simplenotes.app.Server
import be.simplenotes.app.filters.AuthFilter
import be.simplenotes.app.filters.AuthType
import be.simplenotes.app.filters.ErrorFilter
import be.simplenotes.app.routes.Router
import be.simplenotes.app.utils.StaticFileResolver
import be.simplenotes.app.utils.StaticFileResolverImpl
import be.simplenotes.app.views.ErrorView
import be.simplenotes.shared.config.ServerConfig
import org.eclipse.jetty.server.ServerConnector
import org.http4k.core.RequestContexts
import org.http4k.routing.RoutingHttpHandler
import org.http4k.server.ConnectorBuilder
import org.http4k.server.Jetty
import org.http4k.server.asServer
import org.koin.core.qualifier.named
import org.koin.core.qualifier.qualifier
import org.koin.dsl.module
import org.http4k.server.ServerConfig as Http4kServerConfig
val serverModule = module {
single(createdAtStart = true) { Server(get(), get()).start() }
single { get<RoutingHttpHandler>().asServer(get()) }
single<Http4kServerConfig> {
val config = get<ServerConfig>()
val builder: ConnectorBuilder = { server: org.eclipse.jetty.server.Server ->
ServerConnector(server).apply {
port = config.port
host = config.host
}
}
Jetty(config.port, builder)
}
single<StaticFileResolver> { StaticFileResolverImpl(get()) }
single {
Router(
get(),
get(),
get(),
get(),
get(),
get(),
requiredAuth = get(AuthType.Required.qualifier),
optionalAuth = get(AuthType.Optional.qualifier),
errorFilter = get(named("ErrorFilter")),
apiAuth = get(named("apiAuthFilter")),
get()
)()
}
single { RequestContexts() }
single(AuthType.Optional.qualifier) { AuthFilter(get(), AuthType.Optional, get())() }
single(AuthType.Required.qualifier) { AuthFilter(get(), AuthType.Required, get())() }
single(named("ErrorFilter")) { ErrorFilter(get())() }
single { ErrorView(get()) }
}

View File

@ -0,0 +1,22 @@
package be.simplenotes.app.serialization
import kotlinx.serialization.KSerializer
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import java.time.LocalDateTime
internal class LocalDateTimeSerializer : KSerializer<LocalDateTime> {
override val descriptor: SerialDescriptor
get() = PrimitiveSerialDescriptor("LocalDateTime", PrimitiveKind.STRING)
override fun serialize(encoder: Encoder, value: LocalDateTime) {
encoder.encodeString(value.toString())
}
override fun deserialize(decoder: Decoder): LocalDateTime {
TODO("Not implemented, isn't needed")
}
}

View File

@ -0,0 +1,22 @@
package be.simplenotes.app.serialization
import kotlinx.serialization.KSerializer
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import java.util.*
internal class UuidSerializer : KSerializer<UUID> {
override val descriptor: SerialDescriptor
get() = PrimitiveSerialDescriptor("LocalDateTime", PrimitiveKind.STRING)
override fun serialize(encoder: Encoder, value: UUID) {
encoder.encodeString(value.toString())
}
override fun deserialize(decoder: Decoder): UUID {
TODO()
}
}

View File

@ -1,16 +1,17 @@
package be.simplenotes.app.utils
import kotlinx.serialization.json.*
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
interface StaticFileResolver {
fun resolve(name: String): String?
}
class StaticFileResolverImpl : StaticFileResolver {
class StaticFileResolverImpl(json: Json) : StaticFileResolver {
private val mappings: Map<String, String>
init {
val json = Json {}
val manifest = javaClass.getResource("/css-manifest.json").readText()
val manifestObject = json.parseToJsonElement(manifest).jsonObject
val keys = manifestObject.keys

View File

@ -5,4 +5,4 @@ rm app/src/main/resources/static/styles*
yarn --cwd css run css-purge \
&& docker build -t hubv/simplenotes:latest . \
&& docker push hubv/simplenotes:latest
# && docker push hubv/simplenotes:latest

View File

@ -26,7 +26,12 @@ val domainModule = module {
single<PasswordHash> { BcryptPasswordHash() }
single { SimpleJwt(get()) }
single { JwtPayloadExtractor(get()) }
single { NoteService(get(), get(), get(), get()) }
single {
NoteService(get(), get(), get(), get()).apply {
dropAllIndexes()
indexAll()
}
}
single<MarkdownConverter> { MarkdownConverterImpl() }
single<ExportUseCase> { ExportUseCaseImpl(get()) }
single<ExportUseCase> { ExportUseCaseImpl(get(), get()) }
}

View File

@ -12,7 +12,6 @@ import be.simplenotes.domain.usecases.repositories.NoteRepository
import be.simplenotes.domain.usecases.repositories.UserRepository
import be.simplenotes.domain.usecases.search.NoteSearcher
import be.simplenotes.domain.usecases.search.SearchTerms
import kotlinx.serialization.Serializable
import java.util.*
class NoteService(

View File

@ -2,33 +2,16 @@ package be.simplenotes.domain.usecases.export
import be.simplenotes.domain.model.ExportedNote
import be.simplenotes.domain.usecases.repositories.NoteRepository
import kotlinx.serialization.KSerializer
import kotlinx.serialization.builtins.ListSerializer
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.json.Json
import kotlinx.serialization.modules.SerializersModule
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.InputStream
import java.time.LocalDateTime
internal class ExportUseCaseImpl(private val noteRepository: NoteRepository) : ExportUseCase {
internal class ExportUseCaseImpl(private val noteRepository: NoteRepository, private val json: Json) : ExportUseCase {
override fun exportAsJson(userId: Int): String {
val module = SerializersModule {
contextual(LocalDateTime::class, LocalDateTimeSerializer)
}
val json = Json {
prettyPrint = true
serializersModule = module
}
val notes = noteRepository.export(userId)
return json.encodeToString(ListSerializer(ExportedNote.serializer()), notes)
}
@ -65,17 +48,3 @@ class ZipOutput : AutoCloseable {
zipOutputStream.close()
}
}
internal object LocalDateTimeSerializer : KSerializer<LocalDateTime> {
override val descriptor: SerialDescriptor
get() = PrimitiveSerialDescriptor("LocalDateTime", PrimitiveKind.STRING)
override fun serialize(encoder: Encoder, value: LocalDateTime) {
encoder.encodeString(value.toString())
}
override fun deserialize(decoder: Decoder): LocalDateTime {
TODO("Not implemented, isn't needed")
}
}

View File

@ -32,5 +32,8 @@ val persistanceModule = module {
single<NoteRepository> { NoteRepositoryImpl(get()) }
single<DbMigrations> { DbMigrationsImpl(get(), get()) }
single<DataSource> { hikariDataSource(get()) }
single { Database.connect(get<DataSource>()) }
single {
get<DbMigrations>().migrate()
Database.connect(get<DataSource>())
}
}