Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rebuilding the backend with Ktor clean architecture #1

Merged
merged 3 commits into from
Dec 6, 2023
Merged
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 .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
github: [ GroupeMINASTE, NathanFallet, PlugNPush, JohnPoliakov ]
21 changes: 21 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: Test
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Set up our JDK environment
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: 19
- name: Install dependencies and run tests
run: ./gradlew jvmTest koverXmlReport
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
51 changes: 51 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
.gradle
.idea
build/
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**/build/
!**/src/test/**/build/
local.properties

### IntelliJ IDEA ###
.idea/modules.xml
.idea/jarRepositories.xml
.idea/compiler.xml
.idea/libraries/
*.iws
*.iml
*.ipr
out/
!**/src/main/**/out/
!**/src/test/**/out/

### Eclipse ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
bin/
!**/src/main/**/bin/
!**/src/test/**/bin/

### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/

### VS Code ###
.vscode/

### Mac OS ###
.DS_Store

### JS ###
yarn.lock

### Keys
*.pem
*.p8
25 changes: 25 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
plugins {
kotlin("jvm").version("1.9.20").apply(false)
kotlin("plugin.serialization").version("1.9.20").apply(false)
id("convention.publication").apply(false)
id("org.jetbrains.kotlinx.kover").version("0.7.4").apply(false)
id("com.google.devtools.ksp").version("1.9.20-1.0.13").apply(false)
id("dev.petuska.npm.publish").version("3.4.1").apply(false)
}

allprojects {
group = "me.nathanfallet.extopy"
version = "0.1.0"

repositories {
mavenCentral()
}

dependencies {
configurations
.filter { it.name.startsWith("ksp") && it.name.contains("Test") }
.forEach {
add(it.name, "io.mockative:mockative-processor:2.0.1")
}
}
}
2 changes: 2 additions & 0 deletions codecov.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ignore:
- "extopy-commons/src/commonMain/kotlin/me/nathanfallet/extopy/models"
26 changes: 26 additions & 0 deletions contribute.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Contribution guide

## Contribute to code

Task are tracked using GitHub issues.

Contributions are submitted with Pull Requests. Please assign yourself to an issue when you start working on it, so that
we know someone is already working on it, to avoid duplicated contributions.

Repositories:

- [groupeminaste/extopy-backend](https://github.com/groupeminaste/extopy-backend): Backend and API of Extopy
- [groupeminaste/extopy-app](https://github.com/groupeminaste/extopy-app): Mobile apps of Extopy, with a shared module
using KMM

Recommended tools for development:

- Server side: IntelliJ or VS Code with Kotlin and Gradle extensions
- Android app: IntelliJ or Android Studio
- iOS app: Xcode with gradle installed through Homebrew

## Contribute to translations

Base strings (english) are added directly to the repository.

Translations are filled using [Weblate](https://weblate.groupe-minaste.org/projects/extopy/).
7 changes: 7 additions & 0 deletions convention-plugins/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
plugins {
`kotlin-dsl`
}

repositories {
gradlePluginPortal()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import java.util.*

plugins {
`maven-publish`
signing
}

// Stub secrets to let the project sync and build without the publication values set up
ext["signing.keyId"] = null
ext["signing.password"] = null
ext["signing.secretKeyRingFile"] = null
ext["ossrhUsername"] = null
ext["ossrhPassword"] = null

// Grabbing secrets from local.properties file or from environment variables, which could be used on CI
val secretPropsFile = project.rootProject.file("local.properties")
if (secretPropsFile.exists()) {
secretPropsFile.reader().use {
Properties().apply {
load(it)
}
}.onEach { (name, value) ->
ext[name.toString()] = value
}
} else {
ext["signing.keyId"] = System.getenv("SIGNING_KEY_ID")
ext["signing.password"] = System.getenv("SIGNING_PASSWORD")
ext["signing.secretKeyRingFile"] = System.getenv("SIGNING_SECRET_KEY_RING_FILE")
ext["ossrhUsername"] = System.getenv("OSSRH_USERNAME")
ext["ossrhPassword"] = System.getenv("OSSRH_PASSWORD")
}

val javadocJar by tasks.registering(Jar::class) {
archiveClassifier.set("javadoc")
}

fun getExtraString(name: String) = ext[name]?.toString()

publishing {
// Configure maven central repository
repositories {
maven {
name = "sonatype"
setUrl("https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/")
credentials {
username = getExtraString("ossrhUsername")
password = getExtraString("ossrhPassword")
}
}
}

// Configure all publications
publications.withType<MavenPublication> {
// Stub javadoc.jar artifact
artifact(javadocJar.get())

// Provide artifacts information requited by Maven Central
pom {
url.set("https://github.com/groupeminaste/extopy-backend")

licenses {
license {
name.set("GPL-3.0")
url.set("https://opensource.org/licenses/GPL-3.0")
}
}
developers {
developer {
id.set("NathanFallet")
name.set("Nathan Fallet")
email.set("contact@nathanfallet.me")
url.set("https://www.nathanfallet.me")
}
}
scm {
url.set("https://github.com/groupeminaste/extopy-backend.git")
}
}
}
}

// Signing artifacts. Signing.* extra properties values will be used
signing {
sign(publishing.publications)
}

// The Gradle Nexus plugin has a bug that requires us to manually
// configure the signing tasks to run before the publishing tasks.
// see: https://github.com/gradle-nexus/publish-plugin/issues/208
val signingTasks: TaskCollection<Sign> = tasks.withType<Sign>()
tasks.withType<PublishToMavenRepository>().configureEach {
mustRunAfter(signingTasks)
}
91 changes: 91 additions & 0 deletions extopy-backend/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
plugins {
kotlin("multiplatform")
kotlin("plugin.serialization")
id("application")
id("org.jetbrains.kotlinx.kover")
id("com.google.devtools.ksp")
}

application {
mainClass.set("me.nathanfallet.extopy.ApplicationKt")
}

kotlin {
jvm {
jvmToolchain(19)
withJava()
testRuns.named("test") {
executionTask.configure {
useJUnitPlatform()
}
}
}

applyDefaultHierarchyTemplate()

val coroutinesVersion = "1.7.3"
val ktorVersion = "2.3.6"
val koinVersion = "3.5.0"
val exposedVersion = "0.40.1"
val logbackVersion = "0.9.30"
val ktorxVersion = "1.6.1"

sourceSets {
val commonMain by getting {
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion")
implementation("io.ktor:ktor-server-core:$ktorVersion")
implementation("io.ktor:ktor-server-netty:$ktorVersion")
implementation("io.ktor:ktor-server-call-logging:$ktorVersion")
implementation("io.ktor:ktor-server-content-negotiation:$ktorVersion")
implementation("io.ktor:ktor-server-status-pages:$ktorVersion")
implementation("io.ktor:ktor-server-auth:$ktorVersion")
implementation("io.ktor:ktor-server-auth-jwt:$ktorVersion")
implementation("io.ktor:ktor-server-sessions:$ktorVersion")
implementation("io.ktor:ktor-server-websockets:$ktorVersion")
implementation("io.ktor:ktor-server-freemarker:$ktorVersion")
implementation("io.ktor:ktor-serialization-kotlinx-json:$ktorVersion")
implementation("io.ktor:ktor-client-core:$ktorVersion")
implementation("io.ktor:ktor-client-apache:$ktorVersion")
implementation("io.ktor:ktor-client-jetty:$ktorVersion")
implementation("io.ktor:ktor-client-content-negotiation:$ktorVersion")

implementation("io.insert-koin:koin-core:$koinVersion")
implementation("io.insert-koin:koin-ktor:$koinVersion")

implementation("org.jetbrains.exposed:exposed-core:$exposedVersion")
implementation("org.jetbrains.exposed:exposed-jdbc:$exposedVersion")
implementation("org.jetbrains.exposed:exposed-dao:$exposedVersion")

implementation("ch.qos.logback:logback-core:$logbackVersion")
implementation("ch.qos.logback:logback-classic:$logbackVersion")
implementation("org.slf4j:slf4j-api:2.0.7")

implementation("me.nathanfallet.i18n:i18n:1.0.4")
implementation("me.nathanfallet.ktorx:ktor-i18n:$ktorxVersion")
implementation("me.nathanfallet.ktorx:ktor-i18n-freemarker:$ktorxVersion")
implementation("me.nathanfallet.ktorx:ktor-routers:$ktorxVersion")
implementation("me.nathanfallet.ktorx:ktor-routers-locale:$ktorxVersion")
implementation("me.nathanfallet.ktorx:ktor-sentry:$ktorxVersion")
implementation("me.nathanfallet.cloudflare:cloudflare-api-client:4.0.7")

implementation("com.mysql:mysql-connector-j:8.0.33")
implementation("at.favre.lib:bcrypt:0.9.0")
implementation("org.apache.commons:commons-email:1.5")
implementation("io.sentry:sentry:6.34.0")

api(project(":extopy-commons"))
}
}
val jvmTest by getting {
dependencies {
implementation(kotlin("test"))
implementation("io.ktor:ktor-server-test-host:$ktorVersion")
implementation("io.mockative:mockative:2.0.1")
implementation("io.mockk:mockk:1.13.8")
implementation("org.jsoup:jsoup:1.16.2")
implementation("com.h2database:h2:2.2.224")
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package me.nathanfallet.extopy

import io.ktor.server.application.*
import io.ktor.server.netty.*
import me.nathanfallet.extopy.plugins.*

fun main(args: Array<String>): Unit = EngineMain.main(args)

fun Application.module() {
// Initialize plugins
configureI18n()
configureKoin()
configureSerialization()
configureSecurity()
configureSessions()
configureTemplating()
configureRouting()
configureSockets()

configureNotifications()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package me.nathanfallet.extopy.controllers.auth

import io.ktor.server.freemarker.*
import me.nathanfallet.extopy.models.auth.LoginPayload
import me.nathanfallet.extopy.models.auth.RegisterCodePayload
import me.nathanfallet.extopy.models.auth.RegisterPayload
import me.nathanfallet.ktorx.controllers.auth.IAuthWithCodeController
import me.nathanfallet.ktorx.models.auth.AuthMapping
import me.nathanfallet.ktorx.routers.auth.LocalizedAuthWithCodeTemplateRouter
import me.nathanfallet.ktorx.usecases.localization.IGetLocaleForCallUseCase

class AuthRouter(
controller: IAuthWithCodeController<LoginPayload, RegisterPayload, RegisterCodePayload>,
getLocaleForCallUseCase: IGetLocaleForCallUseCase,
) : LocalizedAuthWithCodeTemplateRouter<LoginPayload, RegisterPayload, RegisterCodePayload>(
LoginPayload::class,
RegisterPayload::class,
RegisterCodePayload::class,
AuthMapping(loginTemplate = "auth/login.ftl", registerTemplate = "auth/register.ftl"),
{ template, model -> respondTemplate(template, model) },
controller,
getLocaleForCallUseCase
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package me.nathanfallet.extopy.controllers.notifications

class NotificationsController {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package me.nathanfallet.extopy.controllers.notifications

class NotificationsRouter {
}
Loading