Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 92debb56bb | |||
|
|
eb65cfac51 | ||
|
|
afedd19143 | ||
| f0ff324cf2 | |||
| cf1b78ae0a | |||
| 69e293a25f | |||
| dec6b99281 | |||
| 93aad65385 | |||
| 3effd930ad | |||
| 97b8becd08 | |||
|
|
5e0ba7ed2a | ||
|
|
32a630d6a3 | ||
| c468a5ffa3 | |||
| 3849fa8676 | |||
| 1ee306c151 | |||
| 18e95f7213 | |||
| 82c590dc30 | |||
| da491cecfa | |||
| 3bc5805a87 |
@@ -9,7 +9,7 @@ insert_final_newline = true
|
||||
max_line_length = 120
|
||||
tab_width = 4
|
||||
|
||||
[*.{md,sh,sql,yaml,yml}]
|
||||
[*.{md,sh,sql,xml,xsd,yaml,yml}]
|
||||
max_line_length = 1024
|
||||
indent_size = 2
|
||||
tab_width = 2
|
||||
|
||||
13
.github/workflows/release.yml
vendored
Normal file
13
.github/workflows/release.yml
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
name: Release
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
release:
|
||||
uses: swordsteel/hlaeja-common-workflows/.github/workflows/release.yml@master
|
||||
secrets:
|
||||
CI_BOT_PAT: ${{ secrets.CI_BOT_PAT }}
|
||||
with:
|
||||
TYPE: service
|
||||
DATABASE_FILES: sql
|
||||
12
.github/workflows/run-checks.yml
vendored
Normal file
12
.github/workflows/run-checks.yml
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
name: Pull request validation
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- '.github/**'
|
||||
|
||||
jobs:
|
||||
validate:
|
||||
uses: swordsteel/hlaeja-common-workflows/.github/workflows/run-checks.yml@master
|
||||
secrets:
|
||||
CI_BOT_PAT: ${{ secrets.CI_BOT_PAT }}
|
||||
@@ -16,7 +16,7 @@ In twilight's hush, where mythic tales unfold, A ledger of legends, the bravest
|
||||
|
||||
## Development Configuration
|
||||
|
||||
Run `release.sh` script from `master` branch.
|
||||
Run release pipeline from `master` branch.
|
||||
|
||||
## Development Information
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
plugins {
|
||||
alias(hlaeja.plugins.kotlin.jvm)
|
||||
alias(hlaeja.plugins.kotlin.spring)
|
||||
alias(hlaeja.plugins.ltd.hlaeja.plugin.certificate)
|
||||
alias(hlaeja.plugins.ltd.hlaeja.plugin.service)
|
||||
alias(hlaeja.plugins.spring.boot)
|
||||
alias(hlaeja.plugins.spring.dependency.management)
|
||||
alias(hlaeja.plugins.springframework.boot)
|
||||
alias(hlaeja.plugins.certificate)
|
||||
alias(hlaeja.plugins.service)
|
||||
}
|
||||
|
||||
dependencies {
|
||||
@@ -14,6 +14,7 @@ dependencies {
|
||||
implementation(hlaeja.kotlinx.coroutines)
|
||||
implementation(hlaeja.library.common.messages)
|
||||
implementation(hlaeja.library.jwt)
|
||||
implementation(hlaeja.springboot.kafka)
|
||||
implementation(hlaeja.springboot.starter.actuator)
|
||||
implementation(hlaeja.springboot.starter.r2dbc)
|
||||
implementation(hlaeja.springboot.starter.security)
|
||||
@@ -28,18 +29,19 @@ dependencies {
|
||||
testImplementation(hlaeja.projectreactor.reactor.test)
|
||||
testImplementation(hlaeja.kotlin.test.junit5)
|
||||
testImplementation(hlaeja.kotlinx.coroutines.test)
|
||||
testImplementation(hlaeja.springboot.kafka.test)
|
||||
testImplementation(hlaeja.springboot.starter.test)
|
||||
|
||||
testRuntimeOnly(hlaeja.junit.platform.launcher)
|
||||
|
||||
integrationTestImplementation(hlaeja.assertj.core)
|
||||
integrationTestImplementation(hlaeja.library.test)
|
||||
integrationTestImplementation(hlaeja.projectreactor.reactor.test)
|
||||
integrationTestImplementation(hlaeja.kotlin.test.junit5)
|
||||
integrationTestImplementation(hlaeja.kotlinx.coroutines.test)
|
||||
integrationTestImplementation(hlaeja.springboot.starter.test)
|
||||
testIntegrationImplementation(hlaeja.assertj.core)
|
||||
testIntegrationImplementation(hlaeja.library.test)
|
||||
testIntegrationImplementation(hlaeja.projectreactor.reactor.test)
|
||||
testIntegrationImplementation(hlaeja.kotlin.test.junit5)
|
||||
testIntegrationImplementation(hlaeja.kotlinx.coroutines.test)
|
||||
testIntegrationImplementation(hlaeja.springboot.starter.test)
|
||||
|
||||
integrationTestRuntimeOnly(hlaeja.junit.platform.launcher)
|
||||
testIntegrationRuntimeOnly(hlaeja.junit.platform.launcher)
|
||||
}
|
||||
|
||||
group = "ltd.hlaeja"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
kotlin.code.style=official
|
||||
version=0.2.0
|
||||
catalog=0.9.0
|
||||
version=0.5.0-SNAPSHOT
|
||||
catalog=0.12.0
|
||||
container.port.host=9050
|
||||
|
||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
9
gradlew
vendored
9
gradlew
vendored
@@ -86,8 +86,7 @@ done
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
|
||||
' "$PWD" ) || exit
|
||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
@@ -115,7 +114,7 @@ case "$( uname )" in #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
CLASSPATH="\\\"\\\""
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
@@ -206,7 +205,7 @@ fi
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command:
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# and any embedded shellness will be escaped.
|
||||
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||
# treated as '${Hostname}' itself on the command line.
|
||||
@@ -214,7 +213,7 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-classpath "$CLASSPATH" \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
|
||||
"$@"
|
||||
|
||||
# Stop when "xargs" is not available.
|
||||
|
||||
4
gradlew.bat
vendored
4
gradlew.bat
vendored
@@ -70,11 +70,11 @@ goto fail
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
set CLASSPATH=
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
|
||||
@@ -10,8 +10,8 @@ Content-Type: application/json
|
||||
"password": "p4ssw0rd",
|
||||
"enabled": true,
|
||||
"roles": [
|
||||
"ROLE_ADMIN",
|
||||
"ROLE_TEST"
|
||||
"ADMIN",
|
||||
"TEST"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ Content-Type: application/json
|
||||
"password": "pass",
|
||||
"enabled": true,
|
||||
"roles": [
|
||||
"ROLE_TEST"
|
||||
"TEST"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -36,6 +36,6 @@ Content-Type: application/json
|
||||
"username": "user",
|
||||
"enabled": true,
|
||||
"roles": [
|
||||
"ROLE_TEST"
|
||||
"TEST"
|
||||
]
|
||||
}
|
||||
|
||||
5
http/actuator.http
Normal file
5
http/actuator.http
Normal file
@@ -0,0 +1,5 @@
|
||||
### get actuator
|
||||
GET {{hostname}}/actuator
|
||||
|
||||
### get actuator health
|
||||
GET {{hostname}}/actuator/health
|
||||
89
release.sh
89
release.sh
@@ -1,89 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
### This should be a pipeline, but for this example let use this ###
|
||||
|
||||
check_active_branch() {
|
||||
if [ "$(git rev-parse --abbrev-ref HEAD)" != "$1" ]; then
|
||||
echo "Error: The current branch is not $1."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
check_uncommitted_changes() {
|
||||
if [ -n "$(git status --porcelain)" ]; then
|
||||
echo "Error: There are uncommitted changes in the repository."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
prepare_environment() {
|
||||
git fetch origin
|
||||
}
|
||||
|
||||
check_last_commit() {
|
||||
last_commit_message=$(git log -1 --pretty=format:%s)
|
||||
if [ "$last_commit_message" = "[RELEASE] - bump version" ]; then
|
||||
echo "Warning: Nothing to release!!!"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
check_differences() {
|
||||
if ! git diff --quiet origin/"$1" "$1"; then
|
||||
echo "Error: The branches origin/$1 and $1 have differences."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
un_snapshot_version() {
|
||||
sed -i "s/\($1\s*=\s*[0-9]\+\.[0-9]\+\.[0-9]\+\).*/\1/" gradle.properties
|
||||
}
|
||||
|
||||
current_version() {
|
||||
awk -F '=' '/version\s*=\s*[0-9.]*/ {gsub(/^ +| +$/,"",$2); print $2}' gradle.properties
|
||||
}
|
||||
|
||||
stage_files() {
|
||||
for file in "$@"; do
|
||||
if git diff --exit-code --quiet -- "$file"; then
|
||||
echo "No changes in $file"
|
||||
else
|
||||
git add "$file"
|
||||
echo "Changes in $file staged for commit"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
commit_change() {
|
||||
stage_files gradle.properties
|
||||
git commit -m "[RELEASE] - $1"
|
||||
git push --porcelain origin master
|
||||
}
|
||||
|
||||
add_release_tag() {
|
||||
gitTag="v$(current_version)"
|
||||
git tag -a "$gitTag" -m "Release version $gitTag"
|
||||
git push --porcelain origin "$gitTag"
|
||||
}
|
||||
|
||||
snapshot_version() {
|
||||
new_version="$(current_version | awk -F '.' '{print $1 "." $2+1 ".0"}')"
|
||||
sed -i "s/\(version\s*=\s*\)[0-9.]*/\1$new_version-SNAPSHOT/" gradle.properties
|
||||
}
|
||||
|
||||
# check and prepare for release
|
||||
check_active_branch master
|
||||
check_uncommitted_changes
|
||||
prepare_environment
|
||||
check_last_commit
|
||||
check_differences master
|
||||
|
||||
# un-snapshot version for release
|
||||
un_snapshot_version version
|
||||
un_snapshot_version catalog
|
||||
|
||||
# release changes and prepare for next release
|
||||
commit_change "release version: $(current_version)"
|
||||
add_release_tag
|
||||
snapshot_version
|
||||
commit_change 'bump version'
|
||||
@@ -56,10 +56,12 @@ CREATE DATABASE account_registry
|
||||
WITH
|
||||
OWNER = role_administrator
|
||||
ENCODING = 'UTF8'
|
||||
LC_COLLATE = 'en_US.utf8'
|
||||
LC_CTYPE = 'en_US.utf8'
|
||||
LC_COLLATE = 'en_US.UTF-8'
|
||||
LC_CTYPE = 'en_US.UTF-8'
|
||||
LOCALE_PROVIDER = 'libc'
|
||||
TABLESPACE = pg_default
|
||||
CONNECTION LIMIT = -1
|
||||
IS_TEMPLATE = False;
|
||||
|
||||
COMMENT ON DATABASE account_registry
|
||||
IS 'Primary database for user account registration and identity management';
|
||||
@@ -1,4 +1,5 @@
|
||||
insert into public.accounts (id, created_at, updated_at, enabled, username, password, roles)
|
||||
values ('00000000-0000-7000-0000-000000000001'::uuid, '2000-01-01 00:00:00.000001 +00:00', '2000-01-01 00:00:01.000001 +00:00', true, 'admin', '$2a$12$KoXBoLOANMK11J4xeJHPA.Sy0FG.m8KWk7P4XFsMO.ZbFmFI2DckK', 'ROLE_ADMIN'),
|
||||
('00000000-0000-7000-0000-000000000002'::uuid, '2000-01-01 00:00:00.000001 +00:00', '2000-01-01 00:00:01.000001 +00:00', true, 'user', '$2a$12$KoXBoLOANMK11J4xeJHPA.Sy0FG.m8KWk7P4XFsMO.ZbFmFI2DckK', 'ROLE_USER'),
|
||||
('00000000-0000-7000-0000-000000000003'::uuid, '2000-01-01 00:00:00.000001 +00:00', '2000-01-01 00:00:01.000001 +00:00', false, 'disabled', '$2a$12$KoXBoLOANMK11J4xeJHPA.Sy0FG.m8KWk7P4XFsMO.ZbFmFI2DckK', 'ROLE_USER');
|
||||
values ('00000000-0000-7000-0000-000000000001'::uuid, '2000-01-01 00:00:00.000001 +00:00', '2000-01-01 00:00:01.000001 +00:00', true, 'admin', '$2a$12$KoXBoLOANMK11J4xeJHPA.Sy0FG.m8KWk7P4XFsMO.ZbFmFI2DckK', 'ADMIN'),
|
||||
('00000000-0000-7000-0000-000000000002'::uuid, '2000-01-01 00:00:00.000001 +00:00', '2000-01-01 00:00:01.000001 +00:00', true, 'user', '$2a$12$KoXBoLOANMK11J4xeJHPA.Sy0FG.m8KWk7P4XFsMO.ZbFmFI2DckK', 'USER'),
|
||||
('00000000-0000-7000-0000-000000000002'::uuid, '2000-01-01 00:00:00.000001 +00:00', '2000-01-01 00:00:01.000001 +00:00', true, 'user', '$2a$12$KoXBoLOANMK11J4xeJHPA.Sy0FG.m8KWk7P4XFsMO.ZbFmFI2DckK', 'USER'),
|
||||
('00000000-0000-7000-0000-000000000003'::uuid, '2000-01-01 00:00:00.000001 +00:00', '2000-01-01 00:00:01.000001 +00:00', false, 'disabled', '$2a$12$KoXBoLOANMK11J4xeJHPA.Sy0FG.m8KWk7P4XFsMO.ZbFmFI2DckK', 'USER');
|
||||
6
sql/v0.4.0/001-update_user_roles.sql
Normal file
6
sql/v0.4.0/001-update_user_roles.sql
Normal file
@@ -0,0 +1,6 @@
|
||||
UPDATE public.accounts
|
||||
SET
|
||||
roles = REPLACE(roles, 'ROLE_', ''),
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
WHERE
|
||||
roles LIKE '%ROLE_%';
|
||||
@@ -1,8 +0,0 @@
|
||||
jwt:
|
||||
private-key: cert/valid-private-key.pem
|
||||
|
||||
spring:
|
||||
r2dbc:
|
||||
url: r2dbc:pool:postgresql://localhost:5432/test
|
||||
username: test
|
||||
password: test
|
||||
@@ -1 +0,0 @@
|
||||
eyJhbGciOiJSUzI1NiJ9.eyJpZCI6IjAwMDAwMDAwLTAwMDAtNzAwMC0wMDAwLTAwMDAwMDAwMDAwMSIsInVzZXJuYW1lIjoiYWRtaW4iLCJyb2xlIjoiUk9MRV9BRE1JTiJ9.D6pK86XPWcdu1imV_y_4nAM6R4WEZvJpQ7oGaPAYe0_rg3UWdmVMa8Iw7L21bRgFoyIa7FQBwb_0AXojFVdb2mdOVDeGOwxQZAx23dwqeicOGd8yUMnuBaRSnd7z4P65KPMbbf0NOTQtho0Iv5mBAwFMJoF67sw-yntfx3cD_bfrI-Rf4oZaZsVn38Y2HJBe2sO2QI4e5_7s82ikxac416OX7PcIEgaf3IeEK1fSzSjRG_dyBGT_Jq_vAzVURsSu4ep976kI-k5ZXNE9EMxKu1S-n5c5eiaqo96ObnaSl4eWFik5q8vLhNLYIYO-bQi1xlJKnStwZqtUwlR763Gd5w
|
||||
@@ -1 +0,0 @@
|
||||
eyJhbGciOiJSUzI1NiJ9.eyJpZCI6IjAwMDAwMDAwLTAwMDAtNzAwMC0wMDAwLTAwMDAwMDAwMDAwMiIsInVzZXJuYW1lIjoidXNlciIsInJvbGUiOiJST0xFX1VTRVIifQ.GvZIq0VF9xB8UY3PUGdnc6JNeUXtv4LzHJ56hWSeqUS6BXH0M_QJ5Lu9ndh9_P85CECp3eKrW4fKymGYe-NUXCtrzhr9-SSZLF6D7GRzAJ4yZjVRCOa_dgqe1RGuIZyZpli36z4NPqeBFqtHJ3Cs5rAI-WdvxGfWPgtM2kzpSJ_0zFihp9mVcZBlWP57HlN7-oKzDJWVpO2E17fWZTy-y4pdrIUsff63c256Cy8NhiAgux9aqZTdzaqp9TsXw59bRsS5d0YH7-gJuBd4xctZwgy_41BOcRk2q-nLyLZgWJs1wmCa_zaW0Fj6fjAsYvpdPNegkpIqrHJcQpGd7nE0KQ
|
||||
@@ -1,13 +1,15 @@
|
||||
package ltd.hlaeja.controller
|
||||
|
||||
import java.util.UUID
|
||||
import ltd.hlaeja.validator.ValidAccount
|
||||
import ltd.hlaeja.entity.AccountEntity
|
||||
import ltd.hlaeja.library.accountRegistry.Account
|
||||
import ltd.hlaeja.service.AccountService
|
||||
import ltd.hlaeja.service.PublicEventService
|
||||
import ltd.hlaeja.util.detectChanges
|
||||
import ltd.hlaeja.util.toAccountEntity
|
||||
import ltd.hlaeja.util.toAccountResponse
|
||||
import ltd.hlaeja.util.updateAccountEntity
|
||||
import ltd.hlaeja.validator.ValidAccount
|
||||
import org.springframework.http.HttpStatus.ACCEPTED
|
||||
import org.springframework.http.HttpStatus.CREATED
|
||||
import org.springframework.security.crypto.password.PasswordEncoder
|
||||
@@ -25,6 +27,7 @@ import reactor.core.publisher.Mono
|
||||
class AccountController(
|
||||
private val accountService: AccountService,
|
||||
private val passwordEncoder: PasswordEncoder,
|
||||
private val publicEventService: PublicEventService,
|
||||
) {
|
||||
|
||||
@GetMapping("/account-{uuid}")
|
||||
@@ -40,9 +43,13 @@ class AccountController(
|
||||
): Mono<Account.Response> = accountService.getUserById(uuid)
|
||||
.map { user ->
|
||||
user.updateAccountEntity(request, passwordEncoder)
|
||||
.also { if (hasChange(user, it)) throw ResponseStatusException(ACCEPTED) }
|
||||
.let { it to it.detectChanges(user) }
|
||||
.also { if (it.second.isEmpty()) throw ResponseStatusException(ACCEPTED) }
|
||||
}
|
||||
.flatMap { (updated: AccountEntity, changes: List<String>) ->
|
||||
accountService.updateAccount(updated)
|
||||
.flatMap { publicEventService.updateAccount(it, changes) }
|
||||
}
|
||||
.flatMap { accountService.updateAccount(it) }
|
||||
.map { it.toAccountResponse() }
|
||||
|
||||
@PostMapping("/account")
|
||||
@@ -51,12 +58,4 @@ class AccountController(
|
||||
@RequestBody @ValidAccount request: Account.Request,
|
||||
): Mono<Account.Response> = accountService.addAccount(request.toAccountEntity(passwordEncoder))
|
||||
.map { it.toAccountResponse() }
|
||||
|
||||
private fun hasChange(
|
||||
user: AccountEntity,
|
||||
update: AccountEntity,
|
||||
): Boolean = user.password == update.password &&
|
||||
user.username == update.username &&
|
||||
user.enabled == update.enabled &&
|
||||
user.roles == update.roles
|
||||
}
|
||||
|
||||
25
src/main/kotlin/ltd/hlaeja/service/PublicEventService.kt
Normal file
25
src/main/kotlin/ltd/hlaeja/service/PublicEventService.kt
Normal file
@@ -0,0 +1,25 @@
|
||||
package ltd.hlaeja.service
|
||||
|
||||
import io.github.oshai.kotlinlogging.KotlinLogging
|
||||
import ltd.hlaeja.entity.AccountEntity
|
||||
import ltd.hlaeja.library.accountRegistry.event.AccountMessage
|
||||
import org.springframework.kafka.core.KafkaTemplate
|
||||
import org.springframework.stereotype.Service
|
||||
import reactor.core.publisher.Mono
|
||||
|
||||
private val log = KotlinLogging.logger {}
|
||||
|
||||
@Service
|
||||
class PublicEventService(
|
||||
private val kafkaTemplate: KafkaTemplate<String, AccountMessage>,
|
||||
) {
|
||||
|
||||
fun updateAccount(
|
||||
account: AccountEntity,
|
||||
changes: List<String>,
|
||||
): Mono<AccountEntity> = Mono
|
||||
.fromFuture(kafkaTemplate.send("account", "change", AccountMessage(account.id!!, changes)))
|
||||
.doOnSuccess { log.trace { "Sent Kafka created event for user ${account.id}" } }
|
||||
.doOnError { e -> log.error(e) { "Failed to send Kafka event" } }
|
||||
.thenReturn(account)
|
||||
}
|
||||
25
src/main/kotlin/ltd/hlaeja/util/AccountUtil.kt
Normal file
25
src/main/kotlin/ltd/hlaeja/util/AccountUtil.kt
Normal file
@@ -0,0 +1,25 @@
|
||||
package ltd.hlaeja.util
|
||||
|
||||
import ltd.hlaeja.entity.AccountEntity
|
||||
import org.springframework.http.HttpStatus.ACCEPTED
|
||||
import org.springframework.web.server.ResponseStatusException
|
||||
|
||||
fun AccountEntity.detectChanges(original: AccountEntity): List<String> {
|
||||
val changes: MutableList<String> = mutableListOf()
|
||||
if (original.password != password) {
|
||||
changes.add("password")
|
||||
}
|
||||
if (original.username != username) {
|
||||
changes.add("username")
|
||||
}
|
||||
if (original.enabled != enabled) {
|
||||
changes.add("enabled")
|
||||
}
|
||||
if (original.roles != roles) {
|
||||
changes.add("roles")
|
||||
}
|
||||
if (changes.isEmpty()) {
|
||||
throw ResponseStatusException(ACCEPTED)
|
||||
}
|
||||
return changes
|
||||
}
|
||||
@@ -9,6 +9,25 @@ spring:
|
||||
os:
|
||||
name: "%APP_BUILD_OS_NAME%"
|
||||
version: "%APP_BUILD_OS_VERSION%"
|
||||
r2dbc:
|
||||
username: services
|
||||
kafka:
|
||||
producer:
|
||||
value-serializer: org.springframework.kafka.support.serializer.JsonSerializer
|
||||
|
||||
management:
|
||||
endpoints:
|
||||
access:
|
||||
default: none
|
||||
web:
|
||||
exposure:
|
||||
include: "health,info"
|
||||
endpoint:
|
||||
health:
|
||||
show-details: always
|
||||
access: read_only
|
||||
info:
|
||||
access: read_only
|
||||
|
||||
jwt:
|
||||
private-key: cert/private_key.pem
|
||||
@@ -23,8 +42,9 @@ spring:
|
||||
on-profile: development
|
||||
r2dbc:
|
||||
url: r2dbc:postgresql://localhost:5432/account_registry
|
||||
username: services
|
||||
password: password
|
||||
kafka:
|
||||
bootstrap-servers: localhost:9091
|
||||
|
||||
---
|
||||
##########################
|
||||
@@ -36,14 +56,17 @@ spring:
|
||||
on-profile: docker
|
||||
r2dbc:
|
||||
url: r2dbc:postgresql://PostgreSQL:5432/account_registry
|
||||
username: services
|
||||
password: password
|
||||
kafka:
|
||||
bootstrap-servers: kafka:9092
|
||||
|
||||
---
|
||||
##############################
|
||||
### Production environment ###
|
||||
### Kubernetes environment ###
|
||||
##############################
|
||||
spring:
|
||||
config:
|
||||
activate:
|
||||
on-profile: production
|
||||
on-profile: kubernetes
|
||||
r2dbc:
|
||||
url: r2dbc:postgresql://dependency-postgresql:5432/account_registry
|
||||
|
||||
@@ -7,7 +7,10 @@
|
||||
<root level="INFO">
|
||||
<appender-ref ref="STDOUT"/>
|
||||
</root>
|
||||
<springProfile name="develop|docker">
|
||||
<springProfile name="development">
|
||||
<logger level="TRACE" name="ltd.hlaeja"/>
|
||||
</springProfile>
|
||||
<springProfile name="docker">
|
||||
<logger level="DEBUG" name="ltd.hlaeja"/>
|
||||
</springProfile>
|
||||
</configuration>
|
||||
@@ -1,11 +0,0 @@
|
||||
<configuration>
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
<root level="INFO">
|
||||
<appender-ref ref="STDOUT"/>
|
||||
</root>
|
||||
<logger level="DEBUG" name="ltd.hlaeja"/>
|
||||
</configuration>
|
||||
@@ -2,7 +2,7 @@ package ltd.hlaeja.controller
|
||||
|
||||
import java.util.UUID
|
||||
import ltd.hlaeja.library.accountRegistry.Account
|
||||
import ltd.hlaeja.test.container.PostgresContainer
|
||||
import ltd.hlaeja.test.container.KafkaPostgresTestContainer
|
||||
import org.assertj.core.api.SoftAssertions
|
||||
import org.assertj.core.api.junit.jupiter.InjectSoftAssertions
|
||||
import org.assertj.core.api.junit.jupiter.SoftAssertionsExtension
|
||||
@@ -20,7 +20,7 @@ import org.springframework.http.HttpStatus.CONFLICT
|
||||
import org.springframework.test.web.reactive.server.WebTestClient
|
||||
import org.springframework.test.web.reactive.server.expectBody
|
||||
|
||||
@PostgresContainer
|
||||
@KafkaPostgresTestContainer
|
||||
@SpringBootTest(webEnvironment = RANDOM_PORT)
|
||||
@ExtendWith(SoftAssertionsExtension::class)
|
||||
class AccountEndpoint {
|
||||
@@ -57,7 +57,7 @@ class AccountEndpoint {
|
||||
softly.assertThat(it.responseBody?.username).isEqualTo("admin")
|
||||
softly.assertThat(it.responseBody?.enabled).isTrue
|
||||
softly.assertThat(it.responseBody?.roles?.size).isEqualTo(1)
|
||||
softly.assertThat(it.responseBody?.roles?.get(0)).isEqualTo("ROLE_ADMIN")
|
||||
softly.assertThat(it.responseBody?.roles?.get(0)).isEqualTo("ADMIN")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ class AccountEndpoint {
|
||||
username = "usernameA",
|
||||
password = "abc123",
|
||||
enabled = true,
|
||||
roles = listOf("ROLE_USER", "ROLE_TEST"),
|
||||
roles = listOf("USER", "TEST"),
|
||||
)
|
||||
|
||||
// when
|
||||
@@ -111,8 +111,8 @@ class AccountEndpoint {
|
||||
softly.assertThat(it.responseBody?.username).isEqualTo("usernameA")
|
||||
softly.assertThat(it.responseBody?.enabled).isTrue
|
||||
softly.assertThat(it.responseBody?.roles?.size).isEqualTo(2)
|
||||
softly.assertThat(it.responseBody?.roles).contains("ROLE_USER")
|
||||
softly.assertThat(it.responseBody?.roles).contains("ROLE_TEST")
|
||||
softly.assertThat(it.responseBody?.roles).contains("USER")
|
||||
softly.assertThat(it.responseBody?.roles).contains("TEST")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,7 +124,7 @@ class AccountEndpoint {
|
||||
username = "usernameB",
|
||||
password = null,
|
||||
enabled = false,
|
||||
roles = listOf("ROLE_TEST"),
|
||||
roles = listOf("TEST"),
|
||||
)
|
||||
|
||||
// when
|
||||
@@ -138,7 +138,7 @@ class AccountEndpoint {
|
||||
softly.assertThat(it.responseBody?.username).isEqualTo("usernameB")
|
||||
softly.assertThat(it.responseBody?.enabled).isFalse
|
||||
softly.assertThat(it.responseBody?.roles?.size).isEqualTo(1)
|
||||
softly.assertThat(it.responseBody?.roles).contains("ROLE_TEST")
|
||||
softly.assertThat(it.responseBody?.roles).contains("TEST")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,7 +150,7 @@ class AccountEndpoint {
|
||||
username = "user",
|
||||
password = null,
|
||||
enabled = true,
|
||||
roles = listOf("ROLE_USER"),
|
||||
roles = listOf("USER"),
|
||||
)
|
||||
|
||||
// when
|
||||
@@ -168,7 +168,7 @@ class AccountEndpoint {
|
||||
username = "admin",
|
||||
password = null,
|
||||
enabled = true,
|
||||
roles = listOf("ROLE_USER"),
|
||||
roles = listOf("USER"),
|
||||
)
|
||||
|
||||
// when
|
||||
@@ -186,7 +186,7 @@ class AccountEndpoint {
|
||||
username = "admin",
|
||||
password = null,
|
||||
enabled = true,
|
||||
roles = listOf("ROLE_USER"),
|
||||
roles = listOf("USER"),
|
||||
)
|
||||
|
||||
// when
|
||||
@@ -202,9 +202,9 @@ class AccountEndpoint {
|
||||
|
||||
@ParameterizedTest
|
||||
@CsvSource(
|
||||
"new-user, new-pass, true, 2, ROLE_USER;ROLE_TEST",
|
||||
"admin-user, admin-pass, false, 1, ROLE_ADMIN",
|
||||
"test-user, test-pass, true, 1, ROLE_USER",
|
||||
"new-user, new-pass, true, 2, USER;TEST",
|
||||
"admin-user, admin-pass, false, 1, ADMIN",
|
||||
"test-user, test-pass, true, 1, USER",
|
||||
)
|
||||
fun `success added account`(
|
||||
username: String,
|
||||
@@ -242,8 +242,8 @@ class AccountEndpoint {
|
||||
|
||||
@ParameterizedTest
|
||||
@CsvSource(
|
||||
"'', new-pass, ROLE_TEST",
|
||||
"new-user, '', ROLE_ADMIN",
|
||||
"'', new-pass, TEST",
|
||||
"new-user, '', ADMIN",
|
||||
"new-user, new-pass, ''",
|
||||
)
|
||||
fun `validation fail on empty values`(
|
||||
@@ -276,7 +276,7 @@ class AccountEndpoint {
|
||||
username = "user",
|
||||
password = "new-pass",
|
||||
enabled = true,
|
||||
roles = listOf("ROLE_USER", "ROLE_TEST"),
|
||||
roles = listOf("USER", "TEST"),
|
||||
)
|
||||
|
||||
// when
|
||||
@@ -293,7 +293,7 @@ class AccountEndpoint {
|
||||
username = "user",
|
||||
password = null,
|
||||
enabled = true,
|
||||
roles = listOf("ROLE_USER", "ROLE_TEST"),
|
||||
roles = listOf("USER", "TEST"),
|
||||
)
|
||||
|
||||
// when
|
||||
@@ -1,7 +1,7 @@
|
||||
package ltd.hlaeja.controller
|
||||
|
||||
import ltd.hlaeja.library.accountRegistry.Account
|
||||
import ltd.hlaeja.test.container.PostgresContainer
|
||||
import ltd.hlaeja.test.container.PostgresTestContainer
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
@@ -13,7 +13,7 @@ import org.springframework.boot.test.web.server.LocalServerPort
|
||||
import org.springframework.test.web.reactive.server.WebTestClient
|
||||
import org.springframework.test.web.reactive.server.expectBody
|
||||
|
||||
@PostgresContainer
|
||||
@PostgresTestContainer
|
||||
@SpringBootTest(webEnvironment = RANDOM_PORT)
|
||||
class AccountsEndpoint {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package ltd.hlaeja.controller
|
||||
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import ltd.hlaeja.library.accountRegistry.Authentication
|
||||
import ltd.hlaeja.test.compareToFile
|
||||
import ltd.hlaeja.test.container.PostgresContainer
|
||||
import ltd.hlaeja.test.container.PostgresTestContainer
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.assertj.core.api.SoftAssertions
|
||||
import org.assertj.core.api.junit.jupiter.InjectSoftAssertions
|
||||
import org.assertj.core.api.junit.jupiter.SoftAssertionsExtension
|
||||
@@ -17,7 +17,7 @@ import org.springframework.http.HttpStatus
|
||||
import org.springframework.test.web.reactive.server.WebTestClient
|
||||
import org.springframework.test.web.reactive.server.expectBody
|
||||
|
||||
@PostgresContainer
|
||||
@PostgresTestContainer
|
||||
@SpringBootTest(webEnvironment = RANDOM_PORT)
|
||||
@ExtendWith(SoftAssertionsExtension::class)
|
||||
class AuthenticationEndpoint {
|
||||
19
src/test-integration/resources/application.yml
Normal file
19
src/test-integration/resources/application.yml
Normal file
@@ -0,0 +1,19 @@
|
||||
jwt:
|
||||
private-key: cert/valid-private-key.pem
|
||||
|
||||
spring:
|
||||
r2dbc:
|
||||
url: r2dbc:postgresql://placeholder
|
||||
username: placeholder
|
||||
password: placeholder
|
||||
kafka:
|
||||
consumer:
|
||||
group-id: test-group
|
||||
auto-offset-reset: earliest
|
||||
key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
|
||||
value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer
|
||||
properties:
|
||||
spring.json.trusted.packages: "*"
|
||||
producer:
|
||||
key-serializer: org.apache.kafka.common.serialization.StringSerializer
|
||||
value-serializer: org.springframework.kafka.support.serializer.JsonSerializer
|
||||
@@ -0,0 +1 @@
|
||||
eyJhbGciOiJSUzI1NiJ9.eyJpZCI6IjAwMDAwMDAwLTAwMDAtNzAwMC0wMDAwLTAwMDAwMDAwMDAwMSIsInVzZXJuYW1lIjoiYWRtaW4iLCJyb2xlIjoiQURNSU4ifQ.Z2mc__k9apWjJoZzJ3DWZuDiVN_jpisWtd0ecwrMnk1NrJ5Uw25pgrXPwn2aY0qYFAe0UGbE-4FhjUCxWLkknR0B-2_86IKHmN1A7z8lTqMRkK7qH-71uK0Y3o0kWKn117-FoSKDG-oefQE42NTwsSrzhiaEIzhUd0fsIyKuQCbDRol79rX5cU1HwOI8DHowpNEgvCLW1ogMWJDygq5GDgQI2HmV8vbnO1soFjKzvW3pz0sHWTimhAi76gl5mD_Lv_DdywcQWndwcGEoNj-SgHuKWktaG2_yzkoC9FQqWBgU7tukuycmLkbde_Oagydt2CAfPsBebu4Ac81UHGdUpw
|
||||
@@ -0,0 +1 @@
|
||||
eyJhbGciOiJSUzI1NiJ9.eyJpZCI6IjAwMDAwMDAwLTAwMDAtNzAwMC0wMDAwLTAwMDAwMDAwMDAwMiIsInVzZXJuYW1lIjoidXNlciIsInJvbGUiOiJVU0VSIn0.kpmQYxhkyKsIjo9mJaysBXW0xdv8UjlmNnVsYNfBu-Tdro_0nQFVzhCcjaD6_TUhx2-3vSkvTwDtmMHsP0JC5B43K473o2zQjyHYzCNakPcNHiste9llNj12n5qUCOUMgCKb7ZztLffSIsYlSL7hyRwwmTaz73MDMYvLWAa4AgSNm8JPe3HkTkqRJ4YZ-saKO9Q0Vb9LLftB7T3b9P5kHYqzwISBsRm1rYHRRpGYs5goR2Qax1hLJBbQR4bswaeTRfl3fQ66mIr6mZqiY279wCzzueLuGyJPFzeZQYiQ2JiYRq3H2NyXCsWKCt2bK-YNwol1K3fYLPSq9kap-AGasQ
|
||||
@@ -1,5 +1,5 @@
|
||||
-- Test data
|
||||
insert into public.accounts (id, created_at, updated_at, enabled, username, password, roles)
|
||||
values ('00000000-0000-7000-0000-000000000001'::uuid, '2000-01-01 00:00:00.000001 +00:00', '2000-01-01 00:00:01.000001 +00:00', true, 'admin', '$2a$12$KoXBoLOANMK11J4xeJHPA.Sy0FG.m8KWk7P4XFsMO.ZbFmFI2DckK', 'ROLE_ADMIN'),
|
||||
('00000000-0000-7000-0000-000000000002'::uuid, '2000-01-01 00:00:00.000001 +00:00', '2000-01-01 00:00:01.000001 +00:00', true, 'user', '$2a$12$KoXBoLOANMK11J4xeJHPA.Sy0FG.m8KWk7P4XFsMO.ZbFmFI2DckK', 'ROLE_USER'),
|
||||
('00000000-0000-7000-0000-000000000003'::uuid, '2000-01-01 00:00:00.000001 +00:00', '2000-01-01 00:00:01.000001 +00:00', false, 'disabled', '$2a$12$KoXBoLOANMK11J4xeJHPA.Sy0FG.m8KWk7P4XFsMO.ZbFmFI2DckK', 'ROLE_USER');
|
||||
values ('00000000-0000-7000-0000-000000000001'::uuid, '2000-01-01 00:00:00.000001 +00:00', '2000-01-01 00:00:01.000001 +00:00', true, 'admin', '$2a$12$KoXBoLOANMK11J4xeJHPA.Sy0FG.m8KWk7P4XFsMO.ZbFmFI2DckK', 'ADMIN'),
|
||||
('00000000-0000-7000-0000-000000000002'::uuid, '2000-01-01 00:00:00.000001 +00:00', '2000-01-01 00:00:01.000001 +00:00', true, 'user', '$2a$12$KoXBoLOANMK11J4xeJHPA.Sy0FG.m8KWk7P4XFsMO.ZbFmFI2DckK', 'USER'),
|
||||
('00000000-0000-7000-0000-000000000003'::uuid, '2000-01-01 00:00:00.000001 +00:00', '2000-01-01 00:00:01.000001 +00:00', false, 'disabled', '$2a$12$KoXBoLOANMK11J4xeJHPA.Sy0FG.m8KWk7P4XFsMO.ZbFmFI2DckK', 'USER');
|
||||
@@ -3,8 +3,8 @@ ALTER TABLE accounts DISABLE TRIGGER ALL;
|
||||
ALTER TABLE accounts_audit DISABLE TRIGGER ALL;
|
||||
|
||||
-- Truncate tables
|
||||
TRUNCATE TABLE accounts_audit;
|
||||
TRUNCATE TABLE accounts;
|
||||
TRUNCATE TABLE accounts_audit CASCADE;
|
||||
TRUNCATE TABLE accounts CASCADE;
|
||||
|
||||
-- Enable triggers on the account table
|
||||
ALTER TABLE accounts ENABLE TRIGGER ALL;
|
||||
@@ -72,8 +72,3 @@ CREATE OR REPLACE TRIGGER accounts_audit_trigger
|
||||
ON public.accounts
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION public.accounts_audit();
|
||||
-- Test data
|
||||
insert into public.accounts (id, created_at, updated_at, enabled, username, password, roles)
|
||||
values ('00000000-0000-7000-0000-000000000001'::uuid, '2000-01-01 00:00:00.000001 +00:00', '2000-01-01 00:00:01.000001 +00:00', true, 'admin', '$2a$12$KoXBoLOANMK11J4xeJHPA.Sy0FG.m8KWk7P4XFsMO.ZbFmFI2DckK', 'ROLE_ADMIN'),
|
||||
('00000000-0000-7000-0000-000000000002'::uuid, '2000-01-01 00:00:00.000001 +00:00', '2000-01-01 00:00:01.000001 +00:00', true, 'user', '$2a$12$KoXBoLOANMK11J4xeJHPA.Sy0FG.m8KWk7P4XFsMO.ZbFmFI2DckK', 'ROLE_USER'),
|
||||
('00000000-0000-7000-0000-000000000003'::uuid, '2000-01-01 00:00:00.000001 +00:00', '2000-01-01 00:00:01.000001 +00:00', false, 'disabled', '$2a$12$KoXBoLOANMK11J4xeJHPA.Sy0FG.m8KWk7P4XFsMO.ZbFmFI2DckK', 'ROLE_USER');
|
||||
109
src/test/kotlin/ltd/hlaeja/util/AccountUtilKtTest.kt
Normal file
109
src/test/kotlin/ltd/hlaeja/util/AccountUtilKtTest.kt
Normal file
@@ -0,0 +1,109 @@
|
||||
package ltd.hlaeja.util
|
||||
|
||||
import java.time.LocalDateTime
|
||||
import java.time.ZoneId
|
||||
import java.time.ZonedDateTime
|
||||
import java.util.UUID
|
||||
import ltd.hlaeja.entity.AccountEntity
|
||||
import org.assertj.core.api.SoftAssertions
|
||||
import org.assertj.core.api.junit.jupiter.InjectSoftAssertions
|
||||
import org.assertj.core.api.junit.jupiter.SoftAssertionsExtension
|
||||
import org.junit.jupiter.api.Assertions.assertThrows
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.extension.ExtendWith
|
||||
import org.junit.jupiter.params.ParameterizedTest
|
||||
import org.junit.jupiter.params.provider.CsvSource
|
||||
import org.springframework.web.server.ResponseStatusException
|
||||
|
||||
@ExtendWith(SoftAssertionsExtension::class)
|
||||
class AccountUtilKtTest {
|
||||
companion object {
|
||||
val account = UUID.fromString("00000000-0000-0000-0000-000000000000")
|
||||
val timestamp: ZonedDateTime = ZonedDateTime.of(LocalDateTime.of(2000, 1, 1, 0, 0, 0, 1), ZoneId.of("UTC"))
|
||||
val originalUser = AccountEntity(
|
||||
id = account,
|
||||
createdAt = timestamp,
|
||||
updatedAt = timestamp,
|
||||
enabled = true,
|
||||
username = "username",
|
||||
password = "password",
|
||||
roles = "ROLE_TEST",
|
||||
)
|
||||
}
|
||||
|
||||
@InjectSoftAssertions
|
||||
lateinit var softly: SoftAssertions
|
||||
|
||||
@Test
|
||||
fun `no change detected`() {
|
||||
// given
|
||||
val account = AccountEntity(
|
||||
id = account,
|
||||
createdAt = timestamp,
|
||||
updatedAt = timestamp,
|
||||
enabled = true,
|
||||
username = "username",
|
||||
password = "password",
|
||||
roles = "ROLE_TEST",
|
||||
)
|
||||
|
||||
// then exception
|
||||
assertThrows(ResponseStatusException::class.java) {
|
||||
account.detectChanges(originalUser)
|
||||
}
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@CsvSource(
|
||||
"false, username, password, ROLE_TEST, enabled",
|
||||
"true, change, password, ROLE_TEST, username",
|
||||
"true, username, change, ROLE_TEST, password",
|
||||
"true, username, password, ROLE_CHANGE, roles",
|
||||
)
|
||||
fun `change one thing`(
|
||||
enabled: Boolean,
|
||||
username: String,
|
||||
password: String,
|
||||
roles: String,
|
||||
expected: String,
|
||||
) {
|
||||
// given
|
||||
val account = AccountEntity(
|
||||
id = account,
|
||||
createdAt = timestamp,
|
||||
updatedAt = timestamp,
|
||||
enabled = enabled,
|
||||
username = username,
|
||||
password = password,
|
||||
roles = roles,
|
||||
)
|
||||
|
||||
// when
|
||||
val result = account.detectChanges(originalUser)
|
||||
|
||||
// then
|
||||
softly.assertThat(result).hasSize(1)
|
||||
softly.assertThat(result[0]).isEqualTo(expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `change everything`() {
|
||||
// given
|
||||
val account = AccountEntity(
|
||||
id = account,
|
||||
createdAt = timestamp,
|
||||
updatedAt = timestamp,
|
||||
enabled = false,
|
||||
username = "change",
|
||||
password = "change",
|
||||
roles = "ROLE_CHANGE",
|
||||
)
|
||||
|
||||
// when
|
||||
val result = account.detectChanges(originalUser)
|
||||
|
||||
// then
|
||||
softly.assertThat(result).hasSize(4)
|
||||
softly.assertThat(result).contains("password", "username", "enabled", "roles")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user