Compare commits

..

No commits in common. "cb58a4fbe08d46f9d83f9c9c1d7eb1c099152533" and "bfd562bc60319035034ba482aa2422d41a30e0eb" have entirely different histories.

52 changed files with 143 additions and 195 deletions

10
.gitignore vendored
View File

@ -1,3 +1,12 @@
# Java
.mtj.tmp/
*.class
*.jar
*.war
*.ear
*.nar
hs_err_pid*
# Gradle
build/
.gradle
@ -43,4 +52,3 @@ jspm_packages/
# python
__pycache__

View File

@ -24,17 +24,4 @@ COPY --from=jdkbuilder /myjdk /myjdk
COPY simplenotes-app/build/libs/app-*-all.jar /app/simplenotes.jar
WORKDIR /app
CMD [ \
"/myjdk/bin/java", \
"--add-opens", \
"java.base/java.nio=ALL-UNNAMED", \
"-server", \
"-XX:+UnlockExperimentalVMOptions", \
"-Xms64m", \
"-Xmx256m", \
"-XX:+UseG1GC", \
"-XX:MaxGCPauseMillis=100", \
"-XX:+UseStringDeduplication", \
"-jar", \
"simplenotes.jar" \
]
CMD ["/myjdk/bin/java", "-server", "-XX:+UnlockExperimentalVMOptions", "-Xms64m", "-Xmx256m", "-XX:+UseG1GC", "-XX:MaxGCPauseMillis=100", "-XX:+UseStringDeduplication", "-jar", "simplenotes.jar"]

View File

@ -1,8 +1,7 @@
plugins {
`kotlin-dsl`
kotlin("jvm") version "1.4.10"
id("com.github.johnrengelman.shadow") version "6.1.0" apply false
kotlin("plugin.serialization") version "1.4.10"
id("com.github.johnrengelman.shadow") version "6.1.0"
}
repositories {
@ -12,7 +11,5 @@ repositories {
dependencies {
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.10")
implementation("org.jetbrains.kotlin:kotlin-serialization:1.4.10")
implementation("com.github.jengelman.gradle.plugins:shadow:6.1.0")
implementation("org.jlleitschuh.gradle:ktlint-gradle:9.4.1")
}

View File

@ -2,8 +2,9 @@ package be.simplenotes
import org.gradle.api.DefaultTask
import org.gradle.api.GradleException
import org.gradle.api.tasks.*
import org.gradle.kotlin.dsl.getByType
import org.gradle.api.tasks.InputDirectory
import org.gradle.api.tasks.OutputDirectory
import org.gradle.api.tasks.TaskAction
import java.io.File
import java.lang.ProcessBuilder.Redirect.PIPE
import java.util.concurrent.TimeUnit
@ -11,57 +12,31 @@ import kotlin.concurrent.thread
open class CssTask : DefaultTask() {
private val root = project.parent!!.rootDir
private val viewsProject = project
.parent
?.project(":simplenotes-views")
?: error("Missing :simplenotes-views")
private val root = project.file(".").parent
@get:InputDirectory
val templatesDir = viewsProject.extensions
.getByType<SourceSetContainer>()
.asMap.getOrElse("main") { error("main sources not found") }
.allSource.srcDirs
.find { it.endsWith("kotlin") }
?: error("kotlin sources not found")
private val yarnRoot = File(project.rootDir, "css")
val templatesDir = File(root, "simplenotes-views/src/main/kotlin/be/simplenotes/views")
@get:InputDirectory
val postCssDir = File(project.rootDir, "css/src")
@get:InputFiles
val postCssConfig = listOf(
"tailwind.config.js",
"postcss.config.js",
"package.json"
).map { File(yarnRoot, it) }
@get:OutputDirectory
val outputRootDir = File(project.buildDir, "generated-resources/css")
private val yarnRoot = File(project.rootDir, "css")
private val cssIndex = File(postCssDir, "styles.pcss")
private val cssOutput = File(outputRootDir, "static/styles.css")
private val manifestOutput = File(outputRootDir, "css-manifest.json")
private val purgeGlob = "$templatesDir/**/*.kt"
@TaskAction
fun generateCss() {
// TODO: auto yarn install ?
outputRootDir.deleteRecursively()
outputRootDir.listFiles()?.let { it.forEach { it.delete() } }
ProcessBuilder("yarn", "run", "postcss", "build", "$cssIndex", "--output", "$cssOutput")
.apply {
environment().let {
it["MANIFEST"] = "$manifestOutput"
it["NODE_ENV"] = "production"
it["PURGE"] = purgeGlob
}
}
.apply { environment()["MANIFEST"] = "$manifestOutput" }
.redirectOutput(PIPE)
.redirectError(PIPE)
.directory(yarnRoot)

View File

@ -3,7 +3,7 @@
package be.simplenotes
object Libs {
const val arrowCoreData = "io.arrow-kt:arrow-core-data:0.11.0"
const val arrowCore = "io.arrow-kt:arrow-core:0.11.0"
const val commonsCompress = "org.apache.commons:commons-compress:1.20"
const val flexmark = "com.vladsch.flexmark:flexmark:0.62.2"
const val flexmarkGfmTasklist = "com.vladsch.flexmark:flexmark-ext-gfm-tasklist:0.62.2"

View File

@ -6,7 +6,6 @@ plugins {
java
kotlin("jvm")
`java-library`
id("org.jlleitschuh.gradle.ktlint")
}
repositories {
@ -22,7 +21,6 @@ version = "1.0-SNAPSHOT"
dependencies {
implementation(kotlin("stdlib-jdk8"))
implementation(platform("org.jetbrains.kotlin:kotlin-bom:1.4.10"))
}
tasks.withType<Test> {

View File

@ -1,6 +0,0 @@
package be.simplenotes
plugins {
kotlin("jvm") apply false
kotlin("plugin.serialization")
}

View File

@ -1,3 +1,4 @@
arrow/core/extensions/**
org/checkerframework/**
org/intellij/**
com/google/errorprone/**

View File

@ -1,7 +1,7 @@
module.exports = {
purge: {
content: [
process.env.PURGE
'../simplenotes-app/src/main/kotlin/be/simplenotes/app/views/**/*.kt'
]
},
theme: {

Binary file not shown.

View File

@ -2,7 +2,6 @@ import be.simplenotes.Libs
plugins {
id("be.simplenotes.base")
id("be.simplenotes.kotlinx-serialization")
id("be.simplenotes.app-shadow")
id("be.simplenotes.app-css")
id("be.simplenotes.app-docker")
@ -17,7 +16,7 @@ dependencies {
implementation(project(":simplenotes-views"))
implementation(Libs.koinCore)
implementation(Libs.arrowCoreData)
implementation(Libs.arrowCore)
implementation(Libs.konform)
implementation(Libs.http4kCore)
implementation(Libs.jettyServer)

View File

@ -2,10 +2,10 @@ package be.simplenotes.app.api
import be.simplenotes.app.extensions.auto
import be.simplenotes.app.utils.parseSearchTerms
import be.simplenotes.domain.usecases.NoteService
import be.simplenotes.types.LoggedInUser
import be.simplenotes.types.PersistedNote
import be.simplenotes.types.PersistedNoteMetadata
import be.simplenotes.domain.usecases.NoteService
import be.simplenotes.types.LoggedInUser
import kotlinx.serialization.Contextual
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
@ -28,7 +28,7 @@ class ApiNoteController(private val noteService: NoteService, private val json:
)
}
fun notes(@Suppress("UNUSED_PARAMETER") request: Request, loggedInUser: LoggedInUser): Response {
fun notes(request: Request, loggedInUser: LoggedInUser): Response {
val notes = noteService.paginatedNotes(loggedInUser.userId, page = 1).notes
return persistedNotesMetadataLens(notes, Response(OK))
}
@ -40,15 +40,12 @@ class ApiNoteController(private val noteService: NoteService, private val json:
fun update(request: Request, loggedInUser: LoggedInUser): Response {
val content = noteContentLens(request)
return noteService.update(loggedInUser.userId, uuidLens(request), content).fold(
{
Response(BAD_REQUEST)
},
{
if (it == null) Response(NOT_FOUND)
else Response(OK)
}
)
return noteService.update(loggedInUser.userId, uuidLens(request), content).fold({
Response(BAD_REQUEST)
}, {
if (it == null) Response(NOT_FOUND)
else Response(OK)
})
}
fun search(request: Request, loggedInUser: LoggedInUser): Response {
@ -64,6 +61,7 @@ class ApiNoteController(private val noteService: NoteService, private val json:
private val persistedNotesMetadataLens = json.auto<List<PersistedNoteMetadata>>().toLens()
private val persistedNoteLens = json.auto<PersistedNote>().toLens()
private val uuidLens = Path.uuid().of("uuid")
}
@Serializable

View File

@ -7,6 +7,6 @@ import org.http4k.core.Status.Companion.OK
import org.http4k.core.Status.Companion.SERVICE_UNAVAILABLE
class HealthCheckController(private val dbHealthCheck: DbHealthCheck) {
fun healthCheck(@Suppress("UNUSED_PARAMETER") request: Request) =
fun healthCheck(request: Request) =
if (dbHealthCheck.isOk()) Response(OK) else Response(SERVICE_UNAVAILABLE)
}

View File

@ -3,12 +3,12 @@ package be.simplenotes.app.controllers
import be.simplenotes.app.extensions.html
import be.simplenotes.app.extensions.redirect
import be.simplenotes.app.utils.parseSearchTerms
import be.simplenotes.views.NoteView
import be.simplenotes.domain.usecases.NoteService
import be.simplenotes.domain.usecases.markdown.InvalidMeta
import be.simplenotes.domain.usecases.markdown.MissingMeta
import be.simplenotes.domain.usecases.markdown.ValidationError
import be.simplenotes.types.LoggedInUser
import be.simplenotes.views.NoteView
import org.http4k.core.Method
import org.http4k.core.Request
import org.http4k.core.Response
@ -33,16 +33,8 @@ class NoteController(
return noteService.create(loggedInUser.userId, markdownForm).fold(
{
val html = when (it) {
MissingMeta -> view.noteEditor(
loggedInUser,
error = "Missing note metadata",
textarea = markdownForm
)
InvalidMeta -> view.noteEditor(
loggedInUser,
error = "Invalid note metadata",
textarea = markdownForm
)
MissingMeta -> view.noteEditor(loggedInUser, error = "Missing note metadata", textarea = markdownForm)
InvalidMeta -> view.noteEditor(loggedInUser, error = "Invalid note metadata", textarea = markdownForm)
is ValidationError -> view.noteEditor(
loggedInUser,
validationErrors = it.validationErrors,
@ -113,16 +105,8 @@ class NoteController(
return noteService.update(loggedInUser.userId, note.uuid, markdownForm).fold(
{
val html = when (it) {
MissingMeta -> view.noteEditor(
loggedInUser,
error = "Missing note metadata",
textarea = markdownForm
)
InvalidMeta -> view.noteEditor(
loggedInUser,
error = "Invalid note metadata",
textarea = markdownForm
)
MissingMeta -> view.noteEditor(loggedInUser, error = "Missing note metadata", textarea = markdownForm)
InvalidMeta -> view.noteEditor(loggedInUser, error = "Invalid note metadata", textarea = markdownForm)
is ValidationError -> view.noteEditor(
loggedInUser,
validationErrors = it.validationErrors,

View File

@ -2,11 +2,11 @@ package be.simplenotes.app.controllers
import be.simplenotes.app.extensions.html
import be.simplenotes.app.extensions.redirect
import be.simplenotes.views.SettingView
import be.simplenotes.domain.usecases.UserService
import be.simplenotes.domain.usecases.users.delete.DeleteError
import be.simplenotes.domain.usecases.users.delete.DeleteForm
import be.simplenotes.types.LoggedInUser
import be.simplenotes.views.SettingView
import org.http4k.core.*
import org.http4k.core.body.form
import org.http4k.core.cookie.invalidateCookie
@ -67,10 +67,7 @@ class SettingsController(
Response(Status.OK)
.with(attachment("$filename.json", "application/json"))
.body(userService.exportAsJson(loggedInUser.userId))
} else Response(Status.OK).body(userService.exportAsJson(loggedInUser.userId)).header(
"Content-Type",
"application/json"
)
} else Response(Status.OK).body(userService.exportAsJson(loggedInUser.userId)).header("Content-Type", "application/json")
}
private fun Request.deleteForm(loggedInUser: LoggedInUser) =

View File

@ -3,14 +3,14 @@ package be.simplenotes.app.controllers
import be.simplenotes.app.extensions.html
import be.simplenotes.app.extensions.isSecure
import be.simplenotes.app.extensions.redirect
import be.simplenotes.config.JwtConfig
import be.simplenotes.views.UserView
import be.simplenotes.domain.usecases.UserService
import be.simplenotes.domain.usecases.users.login.*
import be.simplenotes.domain.usecases.users.register.InvalidRegisterForm
import be.simplenotes.domain.usecases.users.register.RegisterForm
import be.simplenotes.domain.usecases.users.register.UserExists
import be.simplenotes.config.JwtConfig
import be.simplenotes.types.LoggedInUser
import be.simplenotes.views.UserView
import org.http4k.core.Method.GET
import org.http4k.core.Request
import org.http4k.core.Response

View File

@ -23,8 +23,7 @@ fun Request.isSecure() = header("X-Forwarded-Proto")?.contains("https") ?: false
val bodyLens = httpBodyRoot(
listOf(Meta(true, "body", ParamMeta.ObjectParam, "body")),
ContentType.APPLICATION_JSON.withNoDirectives(),
ContentNegotiation.StrictNoDirective
ContentType.APPLICATION_JSON.withNoDirectives(), ContentNegotiation.StrictNoDirective
).map(
{ it.payload.asString() },
{ Body(it) }

View File

@ -25,7 +25,7 @@ class AuthFilter(
JwtSource.Header -> it.bearerTokenHeader()
JwtSource.Cookie -> it.bearerTokenCookie()
}
val jwtPayload = token?.let { extractor(token) }
val jwtPayload = token?.let { token -> extractor(token) }
when {
jwtPayload != null -> {
ctx[it][authKey] = jwtPayload

View File

@ -12,12 +12,9 @@ import org.http4k.servlet.asServlet
class Jetty(private val port: Int, private val server: Server) : ServerConfig {
constructor(port: Int = 8000) : this(port, http(port))
constructor(port: Int, vararg inConnectors: ConnectorBuilder) : this(
port,
Server().apply {
inConnectors.forEach { addConnector(it(this)) }
}
)
constructor(port: Int, vararg inConnectors: ConnectorBuilder) : this(port, Server().apply {
inConnectors.forEach { addConnector(it(this)) }
})
override fun toServer(httpHandler: HttpHandler): Http4kServer {
server.insertHandler(httpHandler.toJettyHandler())

View File

@ -10,6 +10,7 @@ import be.simplenotes.app.jetty.Jetty
import be.simplenotes.app.routes.Router
import be.simplenotes.app.utils.StaticFileResolver
import be.simplenotes.app.utils.StaticFileResolverImpl
import be.simplenotes.views.ErrorView
import be.simplenotes.config.ServerConfig
import org.eclipse.jetty.server.ServerConnector
import org.http4k.core.Filter

View File

@ -10,7 +10,7 @@ import java.util.*
internal class UuidSerializer : KSerializer<UUID> {
override val descriptor: SerialDescriptor
get() = PrimitiveSerialDescriptor("UUID", PrimitiveKind.STRING)
get() = PrimitiveSerialDescriptor("LocalDateTime", PrimitiveKind.STRING)
override fun serialize(encoder: Encoder, value: UUID) {
encoder.encodeString(value.toString())

View File

@ -1,8 +1,8 @@
package be.simplenotes.app.filters
import be.simplenotes.config.JwtConfig
import be.simplenotes.domain.security.JwtPayloadExtractor
import be.simplenotes.domain.security.SimpleJwt
import be.simplenotes.config.JwtConfig
import be.simplenotes.types.LoggedInUser
import com.natpryce.hamkrest.assertion.assertThat
import org.http4k.core.*

View File

@ -30,9 +30,7 @@ internal class SearchTermsParserKtTest {
createResult("tag:'example' title:'other' end", title = "other", tag = "example", all = "end"),
createResult(
"tag:'example abc' title:'other with words' this is the end ",
title = "other with words",
tag = "example abc",
all = "this is the end"
title = "other with words", tag = "example abc", all = "this is the end"
),
)

View File

@ -2,7 +2,7 @@ import be.simplenotes.Libs
plugins {
id("be.simplenotes.base")
id("be.simplenotes.kotlinx-serialization")
kotlin("plugin.serialization") version "1.4.10"
}
dependencies {
@ -13,7 +13,7 @@ dependencies {
implementation(Libs.kotlinxSerializationJson)
implementation(Libs.koinCore)
implementation(Libs.arrowCoreData)
implementation(Libs.arrowCore)
implementation(Libs.konform)
implementation(Libs.jbcrypt)
implementation(Libs.javaJwt)

View File

@ -1,7 +1,7 @@
package be.simplenotes.domain.usecases.export
import be.simplenotes.persistance.repositories.NoteRepository
import be.simplenotes.types.ExportedNote
import be.simplenotes.persistance.repositories.NoteRepository
import kotlinx.serialization.builtins.ListSerializer
import kotlinx.serialization.json.Json
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry
@ -31,6 +31,7 @@ internal class ExportUseCaseImpl(private val noteRepository: NoteRepository, pri
}
}
class ZipOutput : AutoCloseable {
val outputStream = ByteArrayOutputStream()
private val zipOutputStream = ZipArchiveOutputStream(outputStream)

View File

@ -12,15 +12,14 @@ internal class DeleteUseCaseImpl(
private val userRepository: UserRepository,
private val passwordHash: PasswordHash,
private val searcher: NoteSearcher,
) : DeleteUseCase {
) : DeleteUseCase {
override fun delete(form: DeleteForm) = either.eager<DeleteError, Unit> {
val user = !UserValidations.validateDelete(form)
val persistedUser = !userRepository.find(user.username).rightIfNotNull { DeleteError.Unregistered }
!Either.conditionally(
passwordHash.verify(user.password, persistedUser.password),
{ DeleteError.WrongPassword },
{ Unit }
)
{ Unit })
!Either.conditionally(userRepository.delete(persistedUser.id), { DeleteError.Unregistered }, { Unit })
searcher.dropIndex(persistedUser.id)
}

View File

@ -3,10 +3,10 @@ package be.simplenotes.domain.usecases.users.register
import arrow.core.Either
import arrow.core.filterOrElse
import arrow.core.leftIfNull
import be.simplenotes.types.PersistedUser
import be.simplenotes.domain.security.PasswordHash
import be.simplenotes.domain.validation.UserValidations
import be.simplenotes.persistance.repositories.UserRepository
import be.simplenotes.types.PersistedUser
internal class RegisterUseCaseImpl(
private val userRepository: UserRepository,

View File

@ -1,8 +1,8 @@
package be.simplenotes.domain.usecases.users.register
import arrow.core.Either
import be.simplenotes.domain.usecases.users.login.LoginForm
import be.simplenotes.types.PersistedUser
import be.simplenotes.domain.usecases.users.login.LoginForm
import io.konform.validation.ValidationErrors
sealed class RegisterError

View File

@ -3,13 +3,13 @@ package be.simplenotes.domain.validation
import arrow.core.Either
import arrow.core.left
import arrow.core.right
import be.simplenotes.types.User
import be.simplenotes.domain.usecases.users.delete.DeleteError
import be.simplenotes.domain.usecases.users.delete.DeleteForm
import be.simplenotes.domain.usecases.users.login.InvalidLoginForm
import be.simplenotes.domain.usecases.users.login.LoginForm
import be.simplenotes.domain.usecases.users.register.InvalidRegisterForm
import be.simplenotes.domain.usecases.users.register.RegisterForm
import be.simplenotes.types.User
import io.konform.validation.Validation
import io.konform.validation.jsonschema.maxLength
import io.konform.validation.jsonschema.minLength

View File

@ -1,7 +1,7 @@
package be.simplenotes.domain.security
import be.simplenotes.config.JwtConfig
import be.simplenotes.domain.usecases.users.login.Token
import be.simplenotes.config.JwtConfig
import be.simplenotes.types.LoggedInUser
import com.auth0.jwt.JWT
import com.auth0.jwt.algorithms.Algorithm

View File

@ -1,12 +1,12 @@
package be.simplenotes.domain.usecases.users.login
import be.simplenotes.config.JwtConfig
import be.simplenotes.types.PersistedUser
import be.simplenotes.domain.security.BcryptPasswordHash
import be.simplenotes.domain.security.SimpleJwt
import be.simplenotes.persistance.repositories.UserRepository
import be.simplenotes.config.JwtConfig
import be.simplenotes.domain.testutils.isLeftOfType
import be.simplenotes.domain.testutils.isRight
import be.simplenotes.persistance.repositories.UserRepository
import be.simplenotes.types.PersistedUser
import com.natpryce.hamkrest.assertion.assertThat
import io.mockk.*
import org.junit.jupiter.api.BeforeEach

View File

@ -1,10 +1,10 @@
package be.simplenotes.domain.usecases.users.register
import be.simplenotes.types.PersistedUser
import be.simplenotes.domain.security.BcryptPasswordHash
import be.simplenotes.domain.testutils.isLeftOfType
import be.simplenotes.domain.testutils.isRight
import be.simplenotes.persistance.repositories.UserRepository
import be.simplenotes.types.PersistedUser
import com.natpryce.hamkrest.assertion.assertThat
import com.natpryce.hamkrest.equalTo
import io.mockk.*

View File

@ -1,8 +1,8 @@
package be.simplenotes.persistance
import be.simplenotes.config.DataSourceConfig
import be.simplenotes.persistance.utils.DbType
import be.simplenotes.persistance.utils.type
import be.simplenotes.config.DataSourceConfig
import me.liuwj.ktorm.database.Database
import me.liuwj.ktorm.database.asIterable
import me.liuwj.ktorm.database.use

View File

@ -1,8 +1,8 @@
package be.simplenotes.persistance
import be.simplenotes.config.DataSourceConfig
import be.simplenotes.persistance.utils.DbType
import be.simplenotes.persistance.utils.type
import be.simplenotes.config.DataSourceConfig
import org.flywaydb.core.Flyway
import javax.sql.DataSource

View File

@ -10,6 +10,7 @@ import org.mapstruct.ReportingPolicy
import java.time.LocalDateTime
import java.util.*
/**
* This is an abstract class because kotlin default methods in interface are not seen as default in kapt
* @see [KT-25960](https://youtrack.jetbrains.com/issue/KT-25960)
@ -34,11 +35,7 @@ internal abstract class NoteConverter {
fun toPersistedNote(entity: NoteEntity, tags: Tags) = PersistedNote(
NoteMetadata(title = entity.title, tags = tags),
entity.markdown,
entity.html,
entity.updatedAt,
entity.uuid,
entity.public
entity.markdown, entity.html, entity.updatedAt, entity.uuid, entity.public
)
@Mappings(
@ -49,15 +46,15 @@ internal abstract class NoteConverter {
abstract fun toExportedNote(entity: NoteEntity, tags: Tags): ExportedNote
fun toEntity(note: Note, uuid: UUID, userId: Int, updatedAt: LocalDateTime) = NoteEntity {
this.title = note.meta.title
this.markdown = note.markdown
this.html = note.html
this.uuid = uuid
this.deleted = false
this.public = false
this.user.id = userId
this.updatedAt = updatedAt
}
this.title = note.meta.title
this.markdown = note.markdown
this.html = note.html
this.uuid = uuid
this.deleted = false
this.public = false
this.user.id = userId
this.updatedAt = updatedAt
}
@Mappings(
Mapping(target = ".", source = "note"),
@ -76,6 +73,7 @@ internal abstract class NoteConverter {
@Mapping(target = "deleted", source = "trash")
abstract fun toEntity(exportedNote: ExportedNote): NoteEntity
}
typealias Tags = List<String>

View File

@ -1,11 +1,11 @@
package be.simplenotes.persistance.notes
import be.simplenotes.persistance.converters.NoteConverter
import be.simplenotes.persistance.repositories.NoteRepository
import be.simplenotes.types.ExportedNote
import be.simplenotes.types.Note
import be.simplenotes.types.PersistedNote
import be.simplenotes.types.PersistedNoteMetadata
import be.simplenotes.persistance.converters.NoteConverter
import be.simplenotes.persistance.repositories.NoteRepository
import me.liuwj.ktorm.database.Database
import me.liuwj.ktorm.dsl.*
import me.liuwj.ktorm.entity.*
@ -211,4 +211,5 @@ internal class NoteRepositoryImpl(private val db: Database, private val converte
.filter { it.noteUuid inList map { note -> note.uuid } }
.groupByTo(HashMap(), { it.note.uuid }, { it.name })
}
}

View File

@ -1,9 +1,9 @@
package be.simplenotes.persistance.users
import be.simplenotes.persistance.converters.UserConverter
import be.simplenotes.persistance.repositories.UserRepository
import be.simplenotes.types.PersistedUser
import be.simplenotes.types.User
import be.simplenotes.persistance.converters.UserConverter
import be.simplenotes.persistance.repositories.UserRepository
import me.liuwj.ktorm.database.Database
import me.liuwj.ktorm.dsl.*
import me.liuwj.ktorm.entity.any

View File

@ -26,14 +26,10 @@ internal class NoteConverterTest {
}
val tags = listOf("a", "b")
val note = converter.toNote(entity, tags)
val expectedNote = Note(
NoteMetadata(
title = "title",
tags = tags,
),
markdown = "md",
html = "html"
)
val expectedNote = Note(NoteMetadata(
title = "title",
tags = tags,
), markdown = "md", html = "html")
assertThat(note).isEqualTo(expectedNote)
}
@ -81,6 +77,7 @@ internal class NoteConverterTest {
)
assertThat(note).isEqualTo(expectedNote)
}
}
@Nested
@ -165,5 +162,7 @@ internal class NoteConverterTest {
.hasFieldOrPropertyWithValue("updatedAt", exportedNote.updatedAt)
.hasFieldOrPropertyWithValue("deleted", true)
}
}
}

View File

@ -1,8 +1,8 @@
package be.simplenotes.persistance.converters
import be.simplenotes.persistance.users.UserEntity
import be.simplenotes.types.PersistedUser
import be.simplenotes.types.User
import be.simplenotes.persistance.users.UserEntity
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.mapstruct.factory.Mappers

View File

@ -1,13 +1,14 @@
package be.simplenotes.persistance.notes
import be.simplenotes.config.DataSourceConfig
import be.simplenotes.persistance.DbMigrations
import be.simplenotes.persistance.converters.NoteConverter
import be.simplenotes.persistance.migrationModule
import be.simplenotes.persistance.persistanceModule
import be.simplenotes.config.DataSourceConfig
import be.simplenotes.persistance.repositories.NoteRepository
import be.simplenotes.persistance.repositories.UserRepository
import be.simplenotes.types.*
import be.simplenotes.types.*
import me.liuwj.ktorm.database.Database
import me.liuwj.ktorm.dsl.eq
import me.liuwj.ktorm.entity.filter

View File

@ -1,11 +1,11 @@
package be.simplenotes.persistance.users
import be.simplenotes.config.DataSourceConfig
import be.simplenotes.types.User
import be.simplenotes.persistance.repositories.UserRepository
import be.simplenotes.persistance.DbMigrations
import be.simplenotes.persistance.migrationModule
import be.simplenotes.persistance.persistanceModule
import be.simplenotes.persistance.repositories.UserRepository
import be.simplenotes.types.User
import be.simplenotes.config.DataSourceConfig
import me.liuwj.ktorm.database.*
import me.liuwj.ktorm.dsl.*
import me.liuwj.ktorm.entity.*

View File

@ -1,6 +1,7 @@
package be.simplenotes.search
import be.simplenotes.types.PersistedNote
import be.simplenotes.search.utils.rmdir
import org.apache.lucene.analysis.standard.StandardAnalyzer
import org.apache.lucene.document.Document
import org.apache.lucene.index.*
@ -89,11 +90,7 @@ internal class NoteSearcherImpl(basePath: Path = Path.of("/tmp", "lucene")) : No
emptyList()
}
override fun dropIndex(userId: Int) {
File(baseFile, userId.toString()).deleteRecursively()
}
override fun dropIndex(userId: Int) = rmdir(File(baseFile, userId.toString()).toPath())
override fun dropAll() {
baseFile.deleteRecursively()
}
override fun dropAll() = rmdir(baseFile.toPath())
}

View File

@ -0,0 +1,29 @@
package be.simplenotes.search.utils
import java.io.IOException
import java.nio.file.FileVisitResult
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.SimpleFileVisitor
import java.nio.file.attribute.BasicFileAttributes
internal fun rmdir(path: Path) {
try {
Files.walkFileTree(
path,
object : SimpleFileVisitor<Path>() {
override fun visitFile(file: Path, attrs: BasicFileAttributes?): FileVisitResult {
Files.delete(file)
return FileVisitResult.CONTINUE
}
override fun postVisitDirectory(dir: Path, exc: IOException?): FileVisitResult {
Files.delete(dir)
return FileVisitResult.CONTINUE
}
}
)
} catch (e: IOException) {
// This is fine
}
}

View File

@ -24,14 +24,12 @@ internal class NoteSearcherImplTest {
content: String = "",
uuid: UUID = UUID.randomUUID(),
): PersistedNote {
val note = PersistedNote(
NoteMetadata(title, tags),
val note = PersistedNote(NoteMetadata(title, tags),
markdown = content,
html = "",
LocalDateTime.MIN,
uuid,
public = false
)
public = false)
searcher.indexNote(1, note)
return note
}

View File

@ -2,7 +2,7 @@ import be.simplenotes.Libs
plugins {
id("be.simplenotes.base")
id("be.simplenotes.kotlinx-serialization")
kotlin("plugin.serialization") version "1.4.10"
}
dependencies {

View File

@ -1,6 +1,7 @@
package be.simplenotes.views
import be.simplenotes.types.LoggedInUser
import be.simplenotes.views.components.noteListHeader
import be.simplenotes.types.PersistedNote
import be.simplenotes.types.PersistedNoteMetadata
import be.simplenotes.views.components.*
@ -37,9 +38,7 @@ class NoteView(styles: String) : View(styles) {
|tags: []
|---
|
""".trimMargin(
"|"
)
""".trimMargin("|")
}
submitButton("Save")
}
@ -129,7 +128,7 @@ class NoteView(styles: String) : View(styles) {
+"You are viewing a public note "
}
hr { }
hr { }
}
div("flex items-center justify-between mb-4") {
@ -152,7 +151,7 @@ class NoteView(styles: String) : View(styles) {
+"/notes/public/${note.uuid}"
}
}
hr { }
hr { }
}
}
@ -183,8 +182,7 @@ class NoteView(styles: String) : View(styles) {
button(
type = ButtonType.submit,
name = if (note.public) "private" else "public",
classes = "font-semibold border-b-4 " +
(if (!note.public) "border-teal-200" else "border-green-500") +
classes = "font-semibold border-b-4 ${if (!note.public) "border-teal-200" else "border-green-500"}" +
" p-2 rounded-r bg-teal-200 text-gray-800"
) {
+"Public"

View File

@ -28,11 +28,9 @@ class SettingView(styles: String) : View(styles) {
section("m-4 p-2 bg-gray-800 rounded flex flex-wrap justify-around items-end") {
form(classes = "m-2", method = FormMethod.post, action = "/export") {
button(
name = "display",
button(name = "display",
classes = "inline btn btn-teal block",
type = submit
) { +"Display my data" }
type = submit) { +"Display my data" }
}
form(classes = "m-2", method = FormMethod.post, action = "/export") {

View File

@ -22,7 +22,7 @@ abstract class View(private val styles: String) {
meta(name = "viewport", content = "width=device-width, initial-scale=1")
title("$title - SimpleNotes")
description?.let { meta(name = "description", content = it) }
link(rel = "preload", href = "/recursive-0.0.1.woff2") {
link(rel = "preload", href = "/recursive-0.0.1.woff2"){
attributes["as"] = "font"
attributes["type"] = "font/woff2"
attributes["crossorigin"] = "anonymous"

View File

@ -4,9 +4,7 @@ import kotlinx.html.*
internal class SUMMARY(consumer: TagConsumer<*>) :
HTMLTag(
"summary",
consumer,
emptyMap(),
"summary", consumer, emptyMap(),
inlineTag = true,
emptyTag = false
),

View File

@ -7,6 +7,4 @@ import java.util.*
private val prettyTime = PrettyTime()
internal fun LocalDateTime.toTimeAgo(): String = prettyTime.format(
Date.from(atZone(ZoneId.systemDefault()).toInstant())
)
internal fun LocalDateTime.toTimeAgo(): String = prettyTime.format(Date.from(atZone(ZoneId.systemDefault()).toInstant()))