mirror of
https://github.com/jlengrand/pluckr.git
synced 2026-03-10 08:41:17 +00:00
Merge pull request #3 from jlengrand/feature/clean-project
feature/clean project
This commit is contained in:
7
.github/workflows/build-backend.yml
vendored
7
.github/workflows/build-backend.yml
vendored
@@ -13,6 +13,7 @@ jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
environment: test
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
@@ -25,3 +26,9 @@ jobs:
|
||||
uses: gradle/gradle-build-action@67421db6bd0bf253fb4bd25b31ebb98943c375e1
|
||||
with:
|
||||
arguments: build
|
||||
env:
|
||||
PLUCKR_PASSWORD: ${{ secrets.PLUCKR_PASSWORD }}
|
||||
- name: Upload coverage reports
|
||||
uses: codecov/codecov-action@v3
|
||||
with:
|
||||
files: build/reports/kover/report.xml
|
||||
30
.github/workflows/build-frontend.yml
vendored
Normal file
30
.github/workflows/build-frontend.yml
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
name: Build frontend
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
environment: test
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [17.x]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
cache: 'npm'
|
||||
cache-dependency-path: '**/package-lock.json'
|
||||
- run: npm ci
|
||||
working-directory: ./src/main/js/pluckr-app
|
||||
- run: npm run build --if-present
|
||||
working-directory: ./src/main/js/pluckr-app
|
||||
37
README.md
37
README.md
@@ -1,5 +1,36 @@
|
||||
# Pluckr
|
||||
|
||||
```
|
||||
./gradlew run
|
||||
```
|
||||
Pluckr is a (currently non-functional) project aiming at helping folks find available trees, plants and spices to pluck from.
|
||||
|
||||
## About the project
|
||||
|
||||
This project is build with [Ktor](https://ktor.io/) for the backend, backed by a PostgreSQL database and using [Exposed](https://github.com/JetBrains/Exposed) to interact with the database.
|
||||
The frontend is buildt with [Open-WC](https://open-wc.org/), [Lit](http://lit.dev/) and [Leaflet](https://leafletjs.com/examples/quick-start/) in Javascript.
|
||||
|
||||
## Running the project locally
|
||||
|
||||
The project is in 3 separate pieces. You need to :
|
||||
|
||||
* Fire up a PostgreSQL database, I use a local Docker image for the moment
|
||||
* Fire the backend. You do this using `./gradlew run`.
|
||||
* By default, the app will run with a H2 in memory database. Change the config file to run another configuration
|
||||
* ( ex: `$./gradlew run --args="-config=src/main/resources/application.test.conf`)
|
||||
* Fire the frontend. It is located in `src/js/pluckr-app`. Run `npm install` and then `npm start`
|
||||
|
||||
## TODOs
|
||||
|
||||
* Adds tests and install [Kover](https://lengrand.fr/kover-code-coverage-plugin-for-kotlin/)
|
||||
* Look into Qonada and Detekt
|
||||
* Automated deployment
|
||||
* Setup local and remote db
|
||||
* Build the freaking project
|
||||
* How to see trends with Detekt?
|
||||
|
||||
## License
|
||||
|
||||
This is a personal project, you may not do anything with it without my permission 😊.
|
||||
All rights reserved.
|
||||
|
||||
## Author
|
||||
|
||||
[Julien Lengrand-Lambert](https://twitter.com/jlengrand)
|
||||
|
||||
@@ -9,6 +9,11 @@ plugins {
|
||||
application
|
||||
kotlin("jvm") version "1.7.0"
|
||||
kotlin("plugin.serialization") version "1.6.21"
|
||||
|
||||
// Clean project
|
||||
id("io.gitlab.arturbosch.detekt").version("1.21.0")
|
||||
id("org.jetbrains.kotlinx.kover") version "0.4.2"
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -34,8 +39,8 @@ dependencies {
|
||||
implementation("io.ktor:ktor-server-call-logging:$ktor_version")
|
||||
implementation("io.ktor:ktor-server-metrics-micrometer:$ktor_version")
|
||||
implementation("io.ktor:ktor-server-cors:$ktor_version")
|
||||
implementation("io.ktor:ktor-server-sessions:$ktor_version")
|
||||
implementation("io.ktor:ktor-server-auth:$ktor_version")
|
||||
implementation("io.ktor:ktor-server-sessions-jvm:2.1.0")
|
||||
|
||||
implementation("org.mindrot:jbcrypt:0.4")
|
||||
|
||||
@@ -47,7 +52,11 @@ dependencies {
|
||||
implementation("io.ktor:ktor-serialization-kotlinx-json:$ktor_version")
|
||||
implementation("org.postgresql:postgresql:$postgresqlVersion")
|
||||
implementation("net.postgis:postgis-jdbc:$postgisVersion")
|
||||
implementation("com.h2database:h2:2.1.214")
|
||||
|
||||
|
||||
testImplementation("io.ktor:ktor-server-tests-jvm:$ktor_version")
|
||||
testImplementation("org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version")
|
||||
testImplementation("org.jetbrains.kotlin:kotlin-test:$kotlin_version")
|
||||
testImplementation("io.ktor:ktor-server-test-host-jvm:2.1.0")
|
||||
}
|
||||
656
config/detekt/detekt.yml
Normal file
656
config/detekt/detekt.yml
Normal file
@@ -0,0 +1,656 @@
|
||||
build:
|
||||
maxIssues: 10
|
||||
excludeCorrectable: false
|
||||
weights:
|
||||
# complexity: 2
|
||||
# LongParameterList: 1
|
||||
# style: 1
|
||||
# comments: 1
|
||||
|
||||
config:
|
||||
validation: true
|
||||
warningsAsErrors: false
|
||||
# when writing own rules with new properties, exclude the property path e.g.: 'my_rule_set,.*>.*>[my_property]'
|
||||
excludes: ''
|
||||
|
||||
console-reports:
|
||||
active: true
|
||||
exclude:
|
||||
- 'ProjectStatisticsReport'
|
||||
- 'ComplexityReport'
|
||||
- 'NotificationReport'
|
||||
- 'FindingsReport'
|
||||
- 'FileBasedFindingsReport'
|
||||
# - 'LiteFindingsReport'
|
||||
|
||||
output-reports:
|
||||
active: true
|
||||
exclude:
|
||||
# - 'TxtOutputReport'
|
||||
# - 'XmlOutputReport'
|
||||
# - 'HtmlOutputReport'
|
||||
# - 'MdOutputReport'
|
||||
|
||||
complexity:
|
||||
active: true
|
||||
ComplexCondition:
|
||||
active: true
|
||||
threshold: 4
|
||||
ComplexInterface:
|
||||
active: false
|
||||
threshold: 10
|
||||
includeStaticDeclarations: false
|
||||
includePrivateDeclarations: false
|
||||
ComplexMethod:
|
||||
active: true
|
||||
threshold: 15
|
||||
ignoreSingleWhenExpression: false
|
||||
ignoreSimpleWhenEntries: false
|
||||
ignoreNestingFunctions: false
|
||||
nestingFunctions:
|
||||
- 'also'
|
||||
- 'apply'
|
||||
- 'forEach'
|
||||
- 'isNotNull'
|
||||
- 'ifNull'
|
||||
- 'let'
|
||||
- 'run'
|
||||
- 'use'
|
||||
- 'with'
|
||||
LabeledExpression:
|
||||
active: false
|
||||
ignoredLabels: []
|
||||
LargeClass:
|
||||
active: true
|
||||
threshold: 600
|
||||
LongMethod:
|
||||
active: true
|
||||
threshold: 60
|
||||
LongParameterList:
|
||||
active: true
|
||||
functionThreshold: 6
|
||||
constructorThreshold: 7
|
||||
ignoreDefaultParameters: false
|
||||
ignoreDataClasses: true
|
||||
ignoreAnnotatedParameter: []
|
||||
MethodOverloading:
|
||||
active: false
|
||||
threshold: 6
|
||||
NamedArguments:
|
||||
active: false
|
||||
threshold: 3
|
||||
ignoreArgumentsMatchingNames: false
|
||||
NestedBlockDepth:
|
||||
active: true
|
||||
threshold: 4
|
||||
NestedScopeFunctions:
|
||||
active: false
|
||||
threshold: 1
|
||||
functions:
|
||||
- 'kotlin.apply'
|
||||
- 'kotlin.run'
|
||||
- 'kotlin.with'
|
||||
- 'kotlin.let'
|
||||
- 'kotlin.also'
|
||||
ReplaceSafeCallChainWithRun:
|
||||
active: false
|
||||
StringLiteralDuplication:
|
||||
active: false
|
||||
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
|
||||
threshold: 3
|
||||
ignoreAnnotation: true
|
||||
excludeStringsWithLessThan5Characters: true
|
||||
ignoreStringsRegex: '$^'
|
||||
TooManyFunctions:
|
||||
active: true
|
||||
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
|
||||
thresholdInFiles: 11
|
||||
thresholdInClasses: 11
|
||||
thresholdInInterfaces: 11
|
||||
thresholdInObjects: 11
|
||||
thresholdInEnums: 11
|
||||
ignoreDeprecated: false
|
||||
ignorePrivate: false
|
||||
ignoreOverridden: false
|
||||
|
||||
coroutines:
|
||||
active: true
|
||||
GlobalCoroutineUsage:
|
||||
active: false
|
||||
InjectDispatcher:
|
||||
active: true
|
||||
dispatcherNames:
|
||||
- 'IO'
|
||||
- 'Default'
|
||||
- 'Unconfined'
|
||||
RedundantSuspendModifier:
|
||||
active: true
|
||||
SleepInsteadOfDelay:
|
||||
active: true
|
||||
SuspendFunWithCoroutineScopeReceiver:
|
||||
active: false
|
||||
SuspendFunWithFlowReturnType:
|
||||
active: true
|
||||
|
||||
empty-blocks:
|
||||
active: true
|
||||
EmptyCatchBlock:
|
||||
active: true
|
||||
allowedExceptionNameRegex: '_|(ignore|expected).*'
|
||||
EmptyClassBlock:
|
||||
active: true
|
||||
EmptyDefaultConstructor:
|
||||
active: true
|
||||
EmptyDoWhileBlock:
|
||||
active: true
|
||||
EmptyElseBlock:
|
||||
active: true
|
||||
EmptyFinallyBlock:
|
||||
active: true
|
||||
EmptyForBlock:
|
||||
active: true
|
||||
EmptyFunctionBlock:
|
||||
active: true
|
||||
ignoreOverridden: false
|
||||
EmptyIfBlock:
|
||||
active: true
|
||||
EmptyInitBlock:
|
||||
active: true
|
||||
EmptyKtFile:
|
||||
active: true
|
||||
EmptySecondaryConstructor:
|
||||
active: true
|
||||
EmptyTryBlock:
|
||||
active: true
|
||||
EmptyWhenBlock:
|
||||
active: true
|
||||
EmptyWhileBlock:
|
||||
active: true
|
||||
|
||||
exceptions:
|
||||
active: true
|
||||
ExceptionRaisedInUnexpectedLocation:
|
||||
active: true
|
||||
methodNames:
|
||||
- 'equals'
|
||||
- 'finalize'
|
||||
- 'hashCode'
|
||||
- 'toString'
|
||||
InstanceOfCheckForException:
|
||||
active: true
|
||||
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
|
||||
NotImplementedDeclaration:
|
||||
active: false
|
||||
ObjectExtendsThrowable:
|
||||
active: false
|
||||
PrintStackTrace:
|
||||
active: true
|
||||
RethrowCaughtException:
|
||||
active: true
|
||||
ReturnFromFinally:
|
||||
active: true
|
||||
ignoreLabeled: false
|
||||
SwallowedException:
|
||||
active: true
|
||||
ignoredExceptionTypes:
|
||||
- 'InterruptedException'
|
||||
- 'MalformedURLException'
|
||||
- 'NumberFormatException'
|
||||
- 'ParseException'
|
||||
allowedExceptionNameRegex: '_|(ignore|expected).*'
|
||||
ThrowingExceptionFromFinally:
|
||||
active: true
|
||||
ThrowingExceptionInMain:
|
||||
active: false
|
||||
ThrowingExceptionsWithoutMessageOrCause:
|
||||
active: true
|
||||
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
|
||||
exceptions:
|
||||
- 'ArrayIndexOutOfBoundsException'
|
||||
- 'Exception'
|
||||
- 'IllegalArgumentException'
|
||||
- 'IllegalMonitorStateException'
|
||||
- 'IllegalStateException'
|
||||
- 'IndexOutOfBoundsException'
|
||||
- 'NullPointerException'
|
||||
- 'RuntimeException'
|
||||
- 'Throwable'
|
||||
ThrowingNewInstanceOfSameException:
|
||||
active: true
|
||||
TooGenericExceptionCaught:
|
||||
active: true
|
||||
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
|
||||
exceptionNames:
|
||||
- 'ArrayIndexOutOfBoundsException'
|
||||
- 'Error'
|
||||
- 'Exception'
|
||||
- 'IllegalMonitorStateException'
|
||||
- 'IndexOutOfBoundsException'
|
||||
- 'NullPointerException'
|
||||
- 'RuntimeException'
|
||||
- 'Throwable'
|
||||
allowedExceptionNameRegex: '_|(ignore|expected).*'
|
||||
TooGenericExceptionThrown:
|
||||
active: true
|
||||
exceptionNames:
|
||||
- 'Error'
|
||||
- 'Exception'
|
||||
- 'RuntimeException'
|
||||
- 'Throwable'
|
||||
|
||||
naming:
|
||||
active: true
|
||||
BooleanPropertyNaming:
|
||||
active: false
|
||||
allowedPattern: '^(is|has|are)'
|
||||
ignoreOverridden: true
|
||||
ClassNaming:
|
||||
active: true
|
||||
classPattern: '[A-Z][a-zA-Z0-9]*'
|
||||
ConstructorParameterNaming:
|
||||
active: true
|
||||
parameterPattern: '[a-z][A-Za-z0-9]*'
|
||||
privateParameterPattern: '[a-z][A-Za-z0-9]*'
|
||||
excludeClassPattern: '$^'
|
||||
ignoreOverridden: true
|
||||
EnumNaming:
|
||||
active: true
|
||||
enumEntryPattern: '[A-Z][_a-zA-Z0-9]*'
|
||||
ForbiddenClassName:
|
||||
active: false
|
||||
forbiddenName: []
|
||||
FunctionMaxLength:
|
||||
active: false
|
||||
maximumFunctionNameLength: 30
|
||||
FunctionMinLength:
|
||||
active: false
|
||||
minimumFunctionNameLength: 3
|
||||
FunctionNaming:
|
||||
active: true
|
||||
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
|
||||
functionPattern: '[a-z][a-zA-Z0-9]*'
|
||||
excludeClassPattern: '$^'
|
||||
ignoreOverridden: true
|
||||
FunctionParameterNaming:
|
||||
active: true
|
||||
parameterPattern: '[a-z][A-Za-z0-9]*'
|
||||
excludeClassPattern: '$^'
|
||||
ignoreOverridden: true
|
||||
InvalidPackageDeclaration:
|
||||
active: true
|
||||
rootPackage: ''
|
||||
requireRootInDeclaration: false
|
||||
LambdaParameterNaming:
|
||||
active: false
|
||||
parameterPattern: '[a-z][A-Za-z0-9]*|_'
|
||||
MatchingDeclarationName:
|
||||
active: true
|
||||
mustBeFirst: true
|
||||
MemberNameEqualsClassName:
|
||||
active: true
|
||||
ignoreOverridden: true
|
||||
NoNameShadowing:
|
||||
active: true
|
||||
NonBooleanPropertyPrefixedWithIs:
|
||||
active: false
|
||||
ObjectPropertyNaming:
|
||||
active: true
|
||||
constantPattern: '[A-Za-z][_A-Za-z0-9]*'
|
||||
propertyPattern: '[A-Za-z][_A-Za-z0-9]*'
|
||||
privatePropertyPattern: '(_)?[A-Za-z][_A-Za-z0-9]*'
|
||||
PackageNaming:
|
||||
active: true
|
||||
packagePattern: '[a-z]+(\.[a-z][A-Za-z0-9]*)*'
|
||||
TopLevelPropertyNaming:
|
||||
active: true
|
||||
constantPattern: '[A-Z][_A-Z0-9]*'
|
||||
propertyPattern: '[A-Za-z][_A-Za-z0-9]*'
|
||||
privatePropertyPattern: '_?[A-Za-z][_A-Za-z0-9]*'
|
||||
VariableMaxLength:
|
||||
active: false
|
||||
maximumVariableNameLength: 64
|
||||
VariableMinLength:
|
||||
active: false
|
||||
minimumVariableNameLength: 1
|
||||
VariableNaming:
|
||||
active: true
|
||||
variablePattern: '[a-z][A-Za-z0-9]*'
|
||||
privateVariablePattern: '(_)?[a-z][A-Za-z0-9]*'
|
||||
excludeClassPattern: '$^'
|
||||
ignoreOverridden: true
|
||||
|
||||
performance:
|
||||
active: true
|
||||
ArrayPrimitive:
|
||||
active: true
|
||||
CouldBeSequence:
|
||||
active: false
|
||||
threshold: 3
|
||||
ForEachOnRange:
|
||||
active: true
|
||||
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
|
||||
SpreadOperator:
|
||||
active: true
|
||||
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
|
||||
UnnecessaryTemporaryInstantiation:
|
||||
active: true
|
||||
|
||||
potential-bugs:
|
||||
active: true
|
||||
AvoidReferentialEquality:
|
||||
active: true
|
||||
forbiddenTypePatterns:
|
||||
- 'kotlin.String'
|
||||
CastToNullableType:
|
||||
active: false
|
||||
Deprecation:
|
||||
active: false
|
||||
DontDowncastCollectionTypes:
|
||||
active: false
|
||||
DoubleMutabilityForCollection:
|
||||
active: true
|
||||
mutableTypes:
|
||||
- 'kotlin.collections.MutableList'
|
||||
- 'kotlin.collections.MutableMap'
|
||||
- 'kotlin.collections.MutableSet'
|
||||
- 'java.util.ArrayList'
|
||||
- 'java.util.LinkedHashSet'
|
||||
- 'java.util.HashSet'
|
||||
- 'java.util.LinkedHashMap'
|
||||
- 'java.util.HashMap'
|
||||
DuplicateCaseInWhenExpression:
|
||||
active: true
|
||||
ElseCaseInsteadOfExhaustiveWhen:
|
||||
active: false
|
||||
EqualsAlwaysReturnsTrueOrFalse:
|
||||
active: true
|
||||
EqualsWithHashCodeExist:
|
||||
active: true
|
||||
ExitOutsideMain:
|
||||
active: false
|
||||
ExplicitGarbageCollectionCall:
|
||||
active: true
|
||||
HasPlatformType:
|
||||
active: true
|
||||
IgnoredReturnValue:
|
||||
active: true
|
||||
restrictToAnnotatedMethods: true
|
||||
returnValueAnnotations:
|
||||
- '*.CheckResult'
|
||||
- '*.CheckReturnValue'
|
||||
ignoreReturnValueAnnotations:
|
||||
- '*.CanIgnoreReturnValue'
|
||||
ignoreFunctionCall: []
|
||||
ImplicitDefaultLocale:
|
||||
active: true
|
||||
ImplicitUnitReturnType:
|
||||
active: false
|
||||
allowExplicitReturnType: true
|
||||
InvalidRange:
|
||||
active: true
|
||||
IteratorHasNextCallsNextMethod:
|
||||
active: true
|
||||
IteratorNotThrowingNoSuchElementException:
|
||||
active: true
|
||||
LateinitUsage:
|
||||
active: false
|
||||
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
|
||||
ignoreOnClassesPattern: ''
|
||||
MapGetWithNotNullAssertionOperator:
|
||||
active: true
|
||||
MissingPackageDeclaration:
|
||||
active: false
|
||||
excludes: ['**/*.kts']
|
||||
MissingWhenCase:
|
||||
active: true
|
||||
allowElseExpression: true
|
||||
NullCheckOnMutableProperty:
|
||||
active: false
|
||||
NullableToStringCall:
|
||||
active: false
|
||||
RedundantElseInWhen:
|
||||
active: true
|
||||
UnconditionalJumpStatementInLoop:
|
||||
active: false
|
||||
UnnecessaryNotNullOperator:
|
||||
active: true
|
||||
UnnecessarySafeCall:
|
||||
active: true
|
||||
UnreachableCatchBlock:
|
||||
active: true
|
||||
UnreachableCode:
|
||||
active: true
|
||||
UnsafeCallOnNullableType:
|
||||
active: true
|
||||
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
|
||||
UnsafeCast:
|
||||
active: true
|
||||
UnusedUnaryOperator:
|
||||
active: true
|
||||
UselessPostfixExpression:
|
||||
active: true
|
||||
WrongEqualsTypeParameter:
|
||||
active: true
|
||||
|
||||
style:
|
||||
active: true
|
||||
CanBeNonNullable:
|
||||
active: false
|
||||
CascadingCallWrapping:
|
||||
active: false
|
||||
includeElvis: true
|
||||
ClassOrdering:
|
||||
active: false
|
||||
CollapsibleIfStatements:
|
||||
active: false
|
||||
DataClassContainsFunctions:
|
||||
active: false
|
||||
conversionFunctionPrefix: 'to'
|
||||
DataClassShouldBeImmutable:
|
||||
active: false
|
||||
DestructuringDeclarationWithTooManyEntries:
|
||||
active: true
|
||||
maxDestructuringEntries: 3
|
||||
EqualsNullCall:
|
||||
active: true
|
||||
EqualsOnSignatureLine:
|
||||
active: false
|
||||
ExplicitCollectionElementAccessMethod:
|
||||
active: false
|
||||
ExplicitItLambdaParameter:
|
||||
active: true
|
||||
ExpressionBodySyntax:
|
||||
active: false
|
||||
includeLineWrapping: false
|
||||
ForbiddenComment:
|
||||
active: true
|
||||
values:
|
||||
- 'FIXME:'
|
||||
- 'STOPSHIP:'
|
||||
- 'TODO:'
|
||||
allowedPatterns: ''
|
||||
customMessage: ''
|
||||
ForbiddenImport:
|
||||
active: false
|
||||
imports: []
|
||||
forbiddenPatterns: ''
|
||||
ForbiddenMethodCall:
|
||||
active: false
|
||||
methods:
|
||||
- 'kotlin.io.print'
|
||||
- 'kotlin.io.println'
|
||||
ForbiddenPublicDataClass:
|
||||
active: true
|
||||
excludes: ['**']
|
||||
ignorePackages:
|
||||
- '*.internal'
|
||||
- '*.internal.*'
|
||||
ForbiddenSuppress:
|
||||
active: false
|
||||
rules: []
|
||||
ForbiddenVoid:
|
||||
active: true
|
||||
ignoreOverridden: false
|
||||
ignoreUsageInGenerics: false
|
||||
FunctionOnlyReturningConstant:
|
||||
active: true
|
||||
ignoreOverridableFunction: true
|
||||
ignoreActualFunction: true
|
||||
excludedFunctions: ''
|
||||
LibraryCodeMustSpecifyReturnType:
|
||||
active: true
|
||||
excludes: ['**']
|
||||
LibraryEntitiesShouldNotBePublic:
|
||||
active: true
|
||||
excludes: ['**']
|
||||
LoopWithTooManyJumpStatements:
|
||||
active: true
|
||||
maxJumpCount: 1
|
||||
MagicNumber:
|
||||
active: false
|
||||
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**', '**/*.kts']
|
||||
ignoreNumbers:
|
||||
- '-1'
|
||||
- '0'
|
||||
- '1'
|
||||
- '2'
|
||||
ignoreHashCodeFunction: true
|
||||
ignorePropertyDeclaration: false
|
||||
ignoreLocalVariableDeclaration: false
|
||||
ignoreConstantDeclaration: true
|
||||
ignoreCompanionObjectPropertyDeclaration: true
|
||||
ignoreAnnotation: false
|
||||
ignoreNamedArgument: true
|
||||
ignoreEnums: false
|
||||
ignoreRanges: false
|
||||
ignoreExtensionFunctions: true
|
||||
MandatoryBracesIfStatements:
|
||||
active: false
|
||||
MandatoryBracesLoops:
|
||||
active: false
|
||||
MaxChainedCallsOnSameLine:
|
||||
active: false
|
||||
maxChainedCalls: 5
|
||||
MaxLineLength:
|
||||
active: true
|
||||
maxLineLength: 120
|
||||
excludePackageStatements: true
|
||||
excludeImportStatements: true
|
||||
excludeCommentStatements: false
|
||||
MayBeConst:
|
||||
active: true
|
||||
ModifierOrder:
|
||||
active: true
|
||||
MultilineLambdaItParameter:
|
||||
active: false
|
||||
NestedClassesVisibility:
|
||||
active: true
|
||||
NoTabs:
|
||||
active: false
|
||||
NullableBooleanCheck:
|
||||
active: false
|
||||
ObjectLiteralToLambda:
|
||||
active: true
|
||||
OptionalAbstractKeyword:
|
||||
active: true
|
||||
OptionalUnit:
|
||||
active: false
|
||||
OptionalWhenBraces:
|
||||
active: false
|
||||
PreferToOverPairSyntax:
|
||||
active: false
|
||||
ProtectedMemberInFinalClass:
|
||||
active: true
|
||||
RedundantExplicitType:
|
||||
active: false
|
||||
RedundantHigherOrderMapUsage:
|
||||
active: true
|
||||
RedundantVisibilityModifierRule:
|
||||
active: false
|
||||
ReturnCount:
|
||||
active: true
|
||||
max: 2
|
||||
excludedFunctions: 'equals'
|
||||
excludeLabeled: false
|
||||
excludeReturnFromLambda: true
|
||||
excludeGuardClauses: false
|
||||
SafeCast:
|
||||
active: true
|
||||
SerialVersionUIDInSerializableClass:
|
||||
active: true
|
||||
SpacingBetweenPackageAndImports:
|
||||
active: false
|
||||
ThrowsCount:
|
||||
active: true
|
||||
max: 2
|
||||
excludeGuardClauses: false
|
||||
TrailingWhitespace:
|
||||
active: false
|
||||
UnderscoresInNumericLiterals:
|
||||
active: false
|
||||
acceptableLength: 4
|
||||
allowNonStandardGrouping: false
|
||||
UnnecessaryAbstractClass:
|
||||
active: true
|
||||
UnnecessaryAnnotationUseSiteTarget:
|
||||
active: false
|
||||
UnnecessaryApply:
|
||||
active: true
|
||||
UnnecessaryBackticks:
|
||||
active: false
|
||||
UnnecessaryFilter:
|
||||
active: true
|
||||
UnnecessaryInheritance:
|
||||
active: true
|
||||
UnnecessaryInnerClass:
|
||||
active: false
|
||||
UnnecessaryLet:
|
||||
active: false
|
||||
UnnecessaryParentheses:
|
||||
active: false
|
||||
UntilInsteadOfRangeTo:
|
||||
active: false
|
||||
UnusedImports:
|
||||
active: false
|
||||
UnusedPrivateClass:
|
||||
active: true
|
||||
UnusedPrivateMember:
|
||||
active: true
|
||||
allowedNames: '(_|ignored|expected|serialVersionUID)'
|
||||
UseAnyOrNoneInsteadOfFind:
|
||||
active: true
|
||||
UseArrayLiteralsInAnnotations:
|
||||
active: true
|
||||
UseCheckNotNull:
|
||||
active: true
|
||||
UseCheckOrError:
|
||||
active: true
|
||||
UseDataClass:
|
||||
active: false
|
||||
allowVars: false
|
||||
UseEmptyCounterpart:
|
||||
active: false
|
||||
UseIfEmptyOrIfBlank:
|
||||
active: false
|
||||
UseIfInsteadOfWhen:
|
||||
active: false
|
||||
UseIsNullOrEmpty:
|
||||
active: true
|
||||
UseOrEmpty:
|
||||
active: true
|
||||
UseRequire:
|
||||
active: true
|
||||
UseRequireNotNull:
|
||||
active: true
|
||||
UselessCallOnNotNull:
|
||||
active: true
|
||||
UtilityClassWithPublicConstructor:
|
||||
active: true
|
||||
VarCouldBeVal:
|
||||
active: true
|
||||
ignoreLateinitVar: false
|
||||
WildcardImport:
|
||||
active: false
|
||||
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
|
||||
excludeImports:
|
||||
- 'java.util.*'
|
||||
@@ -1,4 +1,4 @@
|
||||
ktor_version=2.0.3
|
||||
ktor_version=2.1.0
|
||||
kotlin_version=1.7.0
|
||||
logback_version=1.2.11
|
||||
kotlin.code.style=official
|
||||
|
||||
@@ -7,7 +7,7 @@ const hmr = process.argv.includes('--hmr');
|
||||
export default /** @type {import('@web/dev-server').DevServerConfig} */ ({
|
||||
middleware: [
|
||||
proxy('/api', {
|
||||
target: 'http://localhost:9090',
|
||||
target: 'http://localhost:8080',
|
||||
}),
|
||||
],
|
||||
|
||||
|
||||
@@ -4,11 +4,10 @@ import UserSession
|
||||
import io.ktor.serialization.kotlinx.json.*
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.server.auth.*
|
||||
import io.ktor.server.engine.*
|
||||
import io.ktor.server.netty.*
|
||||
import io.ktor.server.plugins.callloging.*
|
||||
import io.ktor.server.plugins.contentnegotiation.*
|
||||
import io.ktor.server.response.*
|
||||
import io.ktor.server.routing.*
|
||||
import io.ktor.server.sessions.*
|
||||
import kotlinx.serialization.json.Json
|
||||
import nl.lengrand.pluckr.plugins.configureRouting
|
||||
@@ -18,9 +17,22 @@ import org.jetbrains.exposed.sql.StdOutSqlLogger
|
||||
import org.jetbrains.exposed.sql.addLogger
|
||||
import org.jetbrains.exposed.sql.transactions.transaction
|
||||
|
||||
fun Application.myapp(){
|
||||
fun Application.module() {
|
||||
val env = environment.config.propertyOrNull("ktor.environment")?.getString()
|
||||
println("Running in the $env environment")
|
||||
|
||||
val database = initDb()
|
||||
routing {
|
||||
get("/api/environment") {
|
||||
call.respondText(env?: "null")
|
||||
}
|
||||
}
|
||||
|
||||
val database = initDb(
|
||||
environment.config.property("ktor.database.url").getString(),
|
||||
environment.config.property("ktor.database.driver").getString(),
|
||||
environment.config.property("ktor.database.user").getString(),
|
||||
environment.config.property("ktor.database.password").getString(),
|
||||
)
|
||||
|
||||
install(Sessions) {
|
||||
cookie<UserSession>("user_session", SessionStorageMemory()) {
|
||||
@@ -33,9 +45,8 @@ fun Application.myapp(){
|
||||
session<UserSession>("user_session") {
|
||||
println("validating session!")
|
||||
|
||||
|
||||
validate { session ->
|
||||
if(session.name.isNotEmpty()) {
|
||||
if (session.name.isNotEmpty()) {
|
||||
println("Valid!")
|
||||
println(session)
|
||||
session
|
||||
@@ -53,7 +64,7 @@ fun Application.myapp(){
|
||||
}
|
||||
|
||||
// install(CORS)
|
||||
install(ContentNegotiation){
|
||||
install(ContentNegotiation) {
|
||||
json(Json {
|
||||
prettyPrint = true
|
||||
isLenient = true
|
||||
@@ -64,22 +75,14 @@ fun Application.myapp(){
|
||||
configureRouting(database)
|
||||
}
|
||||
|
||||
fun initDb(): Database {
|
||||
val database = Database.connect("jdbc:postgresql://localhost:5432/pluckr", driver = "org.postgresql.Driver",
|
||||
user = "pluckr", password = System.getenv("PLUCKR_PASSWORD"))
|
||||
fun initDb(url: String, driver: String, user: String, password: String): Database {
|
||||
val database = Database.connect(url, driver, user , password )
|
||||
|
||||
transaction {
|
||||
transaction(database) {
|
||||
addLogger(StdOutSqlLogger)
|
||||
SchemaUtils.create(Trees, Users)
|
||||
}
|
||||
return database
|
||||
}
|
||||
|
||||
fun main() {
|
||||
embeddedServer(
|
||||
Netty,
|
||||
module = Application::myapp,
|
||||
port = 9090,
|
||||
host = "0.0.0.0")
|
||||
.start(wait = true)
|
||||
}
|
||||
fun main(args: Array<String>): Unit = io.ktor.server.netty.EngineMain.main(args)
|
||||
|
||||
@@ -32,7 +32,7 @@ data class Tree(
|
||||
val name: String,
|
||||
val description: String?,
|
||||
@Serializable(with = PointSerializer::class)
|
||||
val location : Point
|
||||
val location: Point
|
||||
)
|
||||
|
||||
@Serializable
|
||||
@@ -58,14 +58,20 @@ private fun ResultRow.toTree(): Tree {
|
||||
}
|
||||
|
||||
private fun ResultRow.toUser(): User {
|
||||
return User(this[Users.id], this[Users.username], this[Users.password], this[Users.createdAt].toKotlinLocalDateTime(), this[Users.updatedAt].toKotlinLocalDateTime())
|
||||
return User(
|
||||
this[Users.id],
|
||||
this[Users.username],
|
||||
this[Users.password],
|
||||
this[Users.createdAt].toKotlinLocalDateTime(),
|
||||
this[Users.updatedAt].toKotlinLocalDateTime()
|
||||
)
|
||||
}
|
||||
|
||||
class UserController(private val database: Database){
|
||||
class UserController(private val database: Database) {
|
||||
fun createUser(email: String, zepassword: String) {
|
||||
val salt = gensalt()
|
||||
transaction(database) {
|
||||
Users.insert {
|
||||
Users.insert {
|
||||
it[username] = email
|
||||
it[password] = hashpw(zepassword, salt);
|
||||
}
|
||||
@@ -76,8 +82,8 @@ class UserController(private val database: Database){
|
||||
Will throw NoSuchElementException if there are no results, or IllegalArgumentException if there are more than one
|
||||
*/
|
||||
private fun getUser(email: String): User {
|
||||
val user = transaction(database) {
|
||||
Users.select{ Users.username eq email}.single().toUser()
|
||||
val user = transaction(database) {
|
||||
Users.select { Users.username eq email }.single().toUser()
|
||||
}
|
||||
return user
|
||||
}
|
||||
@@ -90,17 +96,17 @@ class UserController(private val database: Database){
|
||||
}
|
||||
|
||||
class TreeController(private val database: Database) {
|
||||
fun getTrees() : ArrayList<Tree> {
|
||||
val trees : ArrayList<Tree> = arrayListOf()
|
||||
transaction(database){
|
||||
fun getTrees(): ArrayList<Tree> {
|
||||
val trees: ArrayList<Tree> = arrayListOf()
|
||||
transaction(database) {
|
||||
Trees.selectAll().map { trees.add(it.toTree()) }
|
||||
}
|
||||
return trees
|
||||
}
|
||||
|
||||
fun getTrees(bbox: List<Double>?) : ArrayList<Tree> {
|
||||
fun getTrees(bbox: List<Double>?): ArrayList<Tree> {
|
||||
return getTrees()
|
||||
}
|
||||
}
|
||||
|
||||
class AuthenticationException(message:String): Exception(message)
|
||||
class AuthenticationException(message: String) : Exception(message)
|
||||
|
||||
@@ -22,7 +22,7 @@ fun Application.configureRouting(database: Database) {
|
||||
routing {
|
||||
|
||||
authenticate("user_session") {
|
||||
get("/api/authenticated"){
|
||||
get("/api/authenticated") {
|
||||
call.respondText("Hello, ${call.principal<UserSession>()?.name}!")
|
||||
}
|
||||
}
|
||||
@@ -30,12 +30,14 @@ fun Application.configureRouting(database: Database) {
|
||||
post("/api/login") {
|
||||
val formParameters = call.receiveParameters()
|
||||
|
||||
try{
|
||||
val user = userController.getUser(formParameters["username"].toString(), formParameters["password"].toString())
|
||||
try {
|
||||
val user = userController.getUser(
|
||||
formParameters["username"].toString(),
|
||||
formParameters["password"].toString()
|
||||
)
|
||||
call.sessions.set(UserSession(user.username))
|
||||
call.respondRedirect("/")
|
||||
}
|
||||
catch(e: ExposedSQLException){
|
||||
} catch (e: ExposedSQLException) {
|
||||
call.response.status(HttpStatusCode(500, e.message!!))
|
||||
}
|
||||
}
|
||||
@@ -45,16 +47,16 @@ fun Application.configureRouting(database: Database) {
|
||||
call.respondRedirect("/")
|
||||
}
|
||||
|
||||
post("/api/signup"){
|
||||
post("/api/signup") {
|
||||
val formParameters = call.receiveParameters()
|
||||
try{
|
||||
try {
|
||||
userController.createUser(formParameters["username"].toString(), formParameters["password"].toString())
|
||||
call.response.status(HttpStatusCode.OK)
|
||||
}
|
||||
catch(e: ExposedSQLException){ // TODO: Should I leak exceptions here?
|
||||
} catch (e: ExposedSQLException) { // TODO: Should I leak exceptions here?
|
||||
val message = when (e.sqlState) {
|
||||
"23505" ->
|
||||
"User already exists"
|
||||
|
||||
else ->
|
||||
"Unknown error, please retry later"
|
||||
}
|
||||
@@ -63,11 +65,10 @@ fun Application.configureRouting(database: Database) {
|
||||
}
|
||||
|
||||
get("/api/trees") {
|
||||
if(call.request.queryParameters["bbox"] != null){
|
||||
if (call.request.queryParameters["bbox"] != null) {
|
||||
val bbox = call.request.queryParameters["bbox"]?.split(",")?.map { it.toDouble() }
|
||||
call.respond(treeController.getTrees(bbox))
|
||||
}
|
||||
else{
|
||||
} else {
|
||||
call.respond(treeController.getTrees())
|
||||
}
|
||||
}
|
||||
|
||||
16
src/main/resources/application.conf
Normal file
16
src/main/resources/application.conf
Normal file
@@ -0,0 +1,16 @@
|
||||
ktor {
|
||||
environment = test
|
||||
|
||||
deployment {
|
||||
port = 8080
|
||||
}
|
||||
application {
|
||||
modules = [ nl.lengrand.pluckr.ApplicationKt.module ]
|
||||
}
|
||||
database {
|
||||
url = "jdbc:h2:mem:test"
|
||||
driver = "org.h2.Driver"
|
||||
user = "pluckr"
|
||||
password = ${PLUCKR_PASSWORD}
|
||||
}
|
||||
}
|
||||
16
src/main/resources/application.dev.conf
Normal file
16
src/main/resources/application.dev.conf
Normal file
@@ -0,0 +1,16 @@
|
||||
ktor {
|
||||
environment = dev
|
||||
|
||||
deployment {
|
||||
port = 8080
|
||||
}
|
||||
application {
|
||||
modules = [ nl.lengrand.pluckr.ApplicationKt.module ]
|
||||
}
|
||||
database{
|
||||
url = "jdbc:postgresql://localhost:5432/pluckr"
|
||||
driver = "org.postgresql.Driver"
|
||||
user = "pluckr"
|
||||
password = ${PLUCKR_PASSWORD}
|
||||
}
|
||||
}
|
||||
16
src/main/resources/application.test.conf
Normal file
16
src/main/resources/application.test.conf
Normal file
@@ -0,0 +1,16 @@
|
||||
ktor {
|
||||
environment = test
|
||||
|
||||
deployment {
|
||||
port = 8080
|
||||
}
|
||||
application {
|
||||
modules = [ nl.lengrand.pluckr.ApplicationKt.module ]
|
||||
}
|
||||
database{
|
||||
url = "jdbc:h2:mem:test"
|
||||
driver = "org.h2.Driver"
|
||||
user = "pluckr"
|
||||
password = ${PLUCKR_PASSWORD}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,18 @@
|
||||
package nl.lengrand.pluckr
|
||||
|
||||
import io.ktor.server.routing.*
|
||||
import io.ktor.client.request.*
|
||||
import io.ktor.client.statement.*
|
||||
import io.ktor.http.*
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.server.response.*
|
||||
import io.ktor.server.request.*
|
||||
import kotlin.test.*
|
||||
import io.ktor.server.testing.*
|
||||
import nl.lengrand.pluckr.plugins.*
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class ApplicationTest {
|
||||
@Test
|
||||
fun testRoot() = testApplication {
|
||||
application {}
|
||||
val response = client.get("/api/hello")
|
||||
assertEquals(HttpStatusCode.OK, response.status)
|
||||
assertEquals("Hello the World!", response.bodyAsText())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user