mirror of https://github.com/usbharu/Hideout.git
commit
2b23b21ec6
|
@ -122,70 +122,6 @@ jobs:
|
||||||
path: build/test-results
|
path: build/test-results
|
||||||
key: unit-test-report-${{ github.sha }}
|
key: unit-test-report-${{ github.sha }}
|
||||||
|
|
||||||
integration-test:
|
|
||||||
name: Integration Test
|
|
||||||
needs: [ setup ]
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Gradle Wrapper Cache
|
|
||||||
uses: actions/cache@v4.0.2
|
|
||||||
with:
|
|
||||||
path: ~/.gradle/wrapper
|
|
||||||
key: gradle-wrapper-${{ hashFiles('gradle/wrapper/gradle-wrapper.properties') }}
|
|
||||||
|
|
||||||
- name: Dependencies Cache
|
|
||||||
uses: actions/cache@v4.0.2
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
~/.gradle/cache/jars-*
|
|
||||||
~/.gradle/caches/transforms-*
|
|
||||||
~/.gradle/caches/modules-*
|
|
||||||
key: gradle-dependencies-${{ hashFiles('**/*.gradle.kts') }}
|
|
||||||
restore-keys: gradle-dependencies-
|
|
||||||
|
|
||||||
- name: Cache
|
|
||||||
uses: actions/cache@v4.0.2
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
~/.gradle/caches/build-cache-*
|
|
||||||
~/.gradle/caches/[0-9]*.*
|
|
||||||
.gradle
|
|
||||||
key: ${{ runner.os }}-gradle-build-${{ github.workflow }}-${{ github.sha }}
|
|
||||||
restore-keys: ${{ runner.os }}-gradle-build-${{ github.workflow }}-
|
|
||||||
|
|
||||||
- name: Build Cache
|
|
||||||
uses: actions/cache@v4.0.2
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
build
|
|
||||||
key: gradle-build-${{ hashFiles('**/*.gradle.kts') }}-${{ hashFiles('src') }}-${{ github.sha }}
|
|
||||||
|
|
||||||
- name: Set up JDK 21
|
|
||||||
uses: actions/setup-java@v4
|
|
||||||
with:
|
|
||||||
java-version: '21'
|
|
||||||
distribution: 'temurin'
|
|
||||||
|
|
||||||
- name: MongoDB in GitHub Actions
|
|
||||||
uses: supercharge/mongodb-github-action@1.11.0
|
|
||||||
with:
|
|
||||||
mongodb-version: latest
|
|
||||||
|
|
||||||
- name: Unit Test
|
|
||||||
uses: gradle/gradle-build-action@v3.4.1
|
|
||||||
with:
|
|
||||||
arguments: :hideout-core:integrationTest
|
|
||||||
|
|
||||||
- name: Save Test Report
|
|
||||||
if: always()
|
|
||||||
uses: actions/cache/save@v4
|
|
||||||
with:
|
|
||||||
path: build/test-results
|
|
||||||
key: integration-test-report-${{ github.sha }}
|
|
||||||
|
|
||||||
coverage:
|
coverage:
|
||||||
name: Coverage
|
name: Coverage
|
||||||
needs: [ setup ]
|
needs: [ setup ]
|
||||||
|
@ -234,9 +170,9 @@ jobs:
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
|
|
||||||
- name: Run Kover
|
- name: Run Kover
|
||||||
uses: gradle/gradle-build-action@v3.4.1
|
uses: gradle/gradle-build-action@v3.3.2
|
||||||
with:
|
with:
|
||||||
arguments: :hideout-core:koverXmlReport -x :hideout-core:integrationTest -x :hideout-core:e2eTest --rerun-tasks
|
arguments: :hideout-core:koverXmlReport --rerun-tasks
|
||||||
|
|
||||||
- name: Add coverage report to PR
|
- name: Add coverage report to PR
|
||||||
if: always()
|
if: always()
|
||||||
|
@ -255,7 +191,7 @@ jobs:
|
||||||
report-tests:
|
report-tests:
|
||||||
name: Report Tests
|
name: Report Tests
|
||||||
if: success() || failure()
|
if: success() || failure()
|
||||||
needs: [ unit-test,integration-test,e2e-test ]
|
needs: [ unit-test ]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Restore Test Report
|
- name: Restore Test Report
|
||||||
|
@ -264,18 +200,6 @@ jobs:
|
||||||
path: build/test-results
|
path: build/test-results
|
||||||
key: unit-test-report-${{ github.sha }}
|
key: unit-test-report-${{ github.sha }}
|
||||||
|
|
||||||
- name: Restore Test Report
|
|
||||||
uses: actions/cache/restore@v4
|
|
||||||
with:
|
|
||||||
path: build/test-results
|
|
||||||
key: integration-test-report-${{ github.sha }}
|
|
||||||
|
|
||||||
- name: Restore Test Report
|
|
||||||
uses: actions/cache/restore@v4
|
|
||||||
with:
|
|
||||||
path: build/test-results
|
|
||||||
key: e2e-test-report-${{ github.sha }}
|
|
||||||
|
|
||||||
- name: JUnit Test Report
|
- name: JUnit Test Report
|
||||||
uses: mikepenz/action-junit-report@v4
|
uses: mikepenz/action-junit-report@v4
|
||||||
with:
|
with:
|
||||||
|
@ -338,75 +262,3 @@ jobs:
|
||||||
uses: reviewdog/action-suggester@v1
|
uses: reviewdog/action-suggester@v1
|
||||||
with:
|
with:
|
||||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
e2e-test:
|
|
||||||
name: E2E Test
|
|
||||||
needs: [ setup ]
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Gradle Wrapper Cache
|
|
||||||
uses: actions/cache@v4.0.2
|
|
||||||
with:
|
|
||||||
path: ~/.gradle/wrapper
|
|
||||||
key: gradle-wrapper-${{ hashFiles('gradle/wrapper/gradle-wrapper.properties') }}
|
|
||||||
|
|
||||||
- name: Dependencies Cache
|
|
||||||
uses: actions/cache@v4.0.2
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
~/.gradle/cache/jars-*
|
|
||||||
~/.gradle/caches/transforms-*
|
|
||||||
~/.gradle/caches/modules-*
|
|
||||||
key: gradle-dependencies-${{ hashFiles('**/*.gradle.kts') }}
|
|
||||||
restore-keys: gradle-dependencies-
|
|
||||||
|
|
||||||
- name: Cache
|
|
||||||
uses: actions/cache@v4.0.2
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
~/.gradle/caches/build-cache-*
|
|
||||||
~/.gradle/caches/[0-9]*.*
|
|
||||||
.gradle
|
|
||||||
key: ${{ runner.os }}-gradle-build-${{ github.workflow }}-${{ github.sha }}
|
|
||||||
restore-keys: ${{ runner.os }}-gradle-build-${{ github.workflow }}-
|
|
||||||
|
|
||||||
- name: Build Cache
|
|
||||||
uses: actions/cache@v4.0.2
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
build
|
|
||||||
key: gradle-build-${{ hashFiles('**/*.gradle.kts') }}-${{ hashFiles('src') }}-${{ github.sha }}
|
|
||||||
|
|
||||||
- name: Set up JDK 21
|
|
||||||
uses: actions/setup-java@v4
|
|
||||||
with:
|
|
||||||
java-version: '21'
|
|
||||||
distribution: 'temurin'
|
|
||||||
|
|
||||||
- name: MongoDB in GitHub Actions
|
|
||||||
uses: supercharge/mongodb-github-action@1.11.0
|
|
||||||
with:
|
|
||||||
mongodb-version: latest
|
|
||||||
|
|
||||||
- name: setup-chrome
|
|
||||||
id: setup-chrome
|
|
||||||
uses: browser-actions/setup-chrome@v1.7.1
|
|
||||||
|
|
||||||
- name: Add Path
|
|
||||||
run: echo ${{ steps.setup-chrome.outputs.chrome-path }} >> $GITHUB_PATH
|
|
||||||
|
|
||||||
- name: E2E Test
|
|
||||||
uses: gradle/gradle-build-action@v3.4.1
|
|
||||||
with:
|
|
||||||
arguments: :hideout-core:e2eTest
|
|
||||||
|
|
||||||
|
|
||||||
- name: Save Test Report
|
|
||||||
if: always()
|
|
||||||
uses: actions/cache/save@v4
|
|
||||||
with:
|
|
||||||
path: build/test-results
|
|
||||||
key: e2e-test-report-${{ github.sha }}
|
|
||||||
|
|
|
@ -46,3 +46,5 @@ out/
|
||||||
|
|
||||||
*.log
|
*.log
|
||||||
/hideout-core/files/
|
/hideout-core/files/
|
||||||
|
/hideout-core/.kotlin/sessions/
|
||||||
|
/hideout-mastodon/.kotlin/sessions/
|
||||||
|
|
|
@ -16,13 +16,54 @@
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
alias(libs.plugins.kotlin.jvm)
|
alias(libs.plugins.kotlin.jvm)
|
||||||
|
alias(libs.plugins.spring.boot)
|
||||||
|
alias(libs.plugins.kotlin.spring)
|
||||||
|
}
|
||||||
|
|
||||||
|
apply {
|
||||||
|
plugin("io.spring.dependency-management")
|
||||||
|
}
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
maven {
|
||||||
|
url = uri("https://git.usbharu.dev/api/packages/usbharu/maven")
|
||||||
|
}
|
||||||
|
maven {
|
||||||
|
name = "GitHubPackages"
|
||||||
|
url = uri("https://maven.pkg.github.com/usbharu/http-signature")
|
||||||
|
credentials {
|
||||||
|
|
||||||
|
username = project.findProperty("gpr.user") as String? ?: System.getenv("USERNAME")
|
||||||
|
password = project.findProperty("gpr.key") as String? ?: System.getenv("TOKEN")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
maven {
|
||||||
|
name = "GitHubPackages2"
|
||||||
|
url = uri("https://maven.pkg.github.com/multim-dev/emoji-kt")
|
||||||
|
credentials {
|
||||||
|
|
||||||
|
username = project.findProperty("gpr.user") as String? ?: System.getenv("USERNAME")
|
||||||
|
password = project.findProperty("gpr.key") as String? ?: System.getenv("TOKEN")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
configurations {
|
||||||
|
all {
|
||||||
|
exclude("org.springframework.boot", "spring-boot-starter-logging")
|
||||||
|
exclude("ch.qos.logback", "logback-classic")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation("dev.usbharu:hideout-core:0.0.1")
|
implementation("dev.usbharu:hideout-core:0.0.1")
|
||||||
implementation("dev.usbharu:hideout-worker:0.0.1")
|
implementation("dev.usbharu:hideout-mastodon:1.0-SNAPSHOT")
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.register("run") {
|
tasks.register("run") {
|
||||||
dependsOn(gradle.includedBuild("hideout-core").task(":run"))
|
dependsOn(gradle.includedBuild("hideout-core").task(":run"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
springBoot {
|
||||||
|
mainClass = "dev.usbharu.hideout.SpringApplicationKt"
|
||||||
|
}
|
|
@ -1,4 +1,6 @@
|
||||||
org.gradle.parallel=true
|
org.gradle.parallel=true
|
||||||
org.gradle.configureondemand=true
|
org.gradle.configureondemand=true
|
||||||
org.gradle.caching=true
|
org.gradle.caching=true
|
||||||
org.gradle.jvmargs=-Xmx4096m -XX:+HeapDumpOnOutOfMemoryError -XX:+UseParallelGC
|
org.gradle.jvmargs=-Xmx4096m -XX:+HeapDumpOnOutOfMemoryError -XX:+UseParallelGC --add-opens=java.base/java.util.concurrent.locks=ALL-UNNAMED
|
||||||
|
org.gradle.configuration-cache=true
|
||||||
|
org.gradle.configuration-cache.problems=warn
|
|
@ -0,0 +1,21 @@
|
||||||
|
plugins {
|
||||||
|
kotlin("jvm") version "1.9.23"
|
||||||
|
}
|
||||||
|
|
||||||
|
group = "dev.usbharu"
|
||||||
|
version = "1.0-SNAPSHOT"
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
testImplementation(kotlin("test"))
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.test {
|
||||||
|
useJUnitPlatform()
|
||||||
|
}
|
||||||
|
kotlin {
|
||||||
|
jvmToolchain(21)
|
||||||
|
}
|
Binary file not shown.
|
@ -1,7 +1,6 @@
|
||||||
|
#Sat Jun 01 11:47:11 JST 2024
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
|
||||||
networkTimeout=10000
|
|
||||||
validateDistributionUrl=true
|
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
|
@ -55,7 +55,7 @@
|
||||||
# Darwin, MinGW, and NonStop.
|
# Darwin, MinGW, and NonStop.
|
||||||
#
|
#
|
||||||
# (3) This script is generated from the Groovy template
|
# (3) This script is generated from the Groovy template
|
||||||
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||||
# within the Gradle project.
|
# within the Gradle project.
|
||||||
#
|
#
|
||||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||||
|
@ -80,11 +80,13 @@ do
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
# This is normally unused
|
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||||
# shellcheck disable=SC2034
|
|
||||||
|
APP_NAME="Gradle"
|
||||||
APP_BASE_NAME=${0##*/}
|
APP_BASE_NAME=${0##*/}
|
||||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
|
||||||
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
|
|
||||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
MAX_FD=maximum
|
MAX_FD=maximum
|
||||||
|
@ -131,29 +133,22 @@ location of your Java installation."
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
JAVACMD=java
|
JAVACMD=java
|
||||||
if ! command -v java >/dev/null 2>&1
|
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
then
|
|
||||||
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
|
||||||
|
|
||||||
Please set the JAVA_HOME variable in your environment to match the
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
location of your Java installation."
|
location of your Java installation."
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Increase the maximum file descriptors if we can.
|
# Increase the maximum file descriptors if we can.
|
||||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||||
case $MAX_FD in #(
|
case $MAX_FD in #(
|
||||||
max*)
|
max*)
|
||||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
|
||||||
# shellcheck disable=SC2039,SC3045
|
|
||||||
MAX_FD=$( ulimit -H -n ) ||
|
MAX_FD=$( ulimit -H -n ) ||
|
||||||
warn "Could not query maximum file descriptor limit"
|
warn "Could not query maximum file descriptor limit"
|
||||||
esac
|
esac
|
||||||
case $MAX_FD in #(
|
case $MAX_FD in #(
|
||||||
'' | soft) :;; #(
|
'' | soft) :;; #(
|
||||||
*)
|
*)
|
||||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
|
||||||
# shellcheck disable=SC2039,SC3045
|
|
||||||
ulimit -n "$MAX_FD" ||
|
ulimit -n "$MAX_FD" ||
|
||||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||||
esac
|
esac
|
||||||
|
@ -198,15 +193,11 @@ if "$cygwin" || "$msys" ; then
|
||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Collect all arguments for the java command;
|
||||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
# shell script including quotes and variable substitutions, so put them in
|
||||||
|
# double quotes to make sure that they get re-expanded; and
|
||||||
# Collect all arguments for the java command:
|
# * put everything else in single quotes, so that it's not re-expanded.
|
||||||
# * DEFAULT_JVM_OPTS, JAVA_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.
|
|
||||||
|
|
||||||
set -- \
|
set -- \
|
||||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||||
|
@ -214,12 +205,6 @@ set -- \
|
||||||
org.gradle.wrapper.GradleWrapperMain \
|
org.gradle.wrapper.GradleWrapperMain \
|
||||||
"$@"
|
"$@"
|
||||||
|
|
||||||
# Stop when "xargs" is not available.
|
|
||||||
if ! command -v xargs >/dev/null 2>&1
|
|
||||||
then
|
|
||||||
die "xargs is not available"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Use "xargs" to parse quoted args.
|
# Use "xargs" to parse quoted args.
|
||||||
#
|
#
|
||||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
|
@ -14,7 +14,7 @@
|
||||||
@rem limitations under the License.
|
@rem limitations under the License.
|
||||||
@rem
|
@rem
|
||||||
|
|
||||||
@if "%DEBUG%"=="" @echo off
|
@if "%DEBUG%" == "" @echo off
|
||||||
@rem ##########################################################################
|
@rem ##########################################################################
|
||||||
@rem
|
@rem
|
||||||
@rem Gradle startup script for Windows
|
@rem Gradle startup script for Windows
|
||||||
|
@ -25,8 +25,7 @@
|
||||||
if "%OS%"=="Windows_NT" setlocal
|
if "%OS%"=="Windows_NT" setlocal
|
||||||
|
|
||||||
set DIRNAME=%~dp0
|
set DIRNAME=%~dp0
|
||||||
if "%DIRNAME%"=="" set DIRNAME=.
|
if "%DIRNAME%" == "" set DIRNAME=.
|
||||||
@rem This is normally unused
|
|
||||||
set APP_BASE_NAME=%~n0
|
set APP_BASE_NAME=%~n0
|
||||||
set APP_HOME=%DIRNAME%
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
|
@ -41,13 +40,13 @@ if defined JAVA_HOME goto findJavaFromJavaHome
|
||||||
|
|
||||||
set JAVA_EXE=java.exe
|
set JAVA_EXE=java.exe
|
||||||
%JAVA_EXE% -version >NUL 2>&1
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
if %ERRORLEVEL% equ 0 goto execute
|
if "%ERRORLEVEL%" == "0" goto execute
|
||||||
|
|
||||||
echo. 1>&2
|
echo.
|
||||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
echo. 1>&2
|
echo.
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
echo location of your Java installation. 1>&2
|
echo location of your Java installation.
|
||||||
|
|
||||||
goto fail
|
goto fail
|
||||||
|
|
||||||
|
@ -57,11 +56,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||||
|
|
||||||
if exist "%JAVA_EXE%" goto execute
|
if exist "%JAVA_EXE%" goto execute
|
||||||
|
|
||||||
echo. 1>&2
|
echo.
|
||||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||||
echo. 1>&2
|
echo.
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
echo location of your Java installation. 1>&2
|
echo location of your Java installation.
|
||||||
|
|
||||||
goto fail
|
goto fail
|
||||||
|
|
||||||
|
@ -76,15 +75,13 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||||
|
|
||||||
:end
|
:end
|
||||||
@rem End local scope for the variables with windows NT shell
|
@rem End local scope for the variables with windows NT shell
|
||||||
if %ERRORLEVEL% equ 0 goto mainEnd
|
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||||
|
|
||||||
:fail
|
:fail
|
||||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||||
rem the _cmd.exe /c_ return code!
|
rem the _cmd.exe /c_ return code!
|
||||||
set EXIT_CODE=%ERRORLEVEL%
|
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||||
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
exit /b 1
|
||||||
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
|
||||||
exit /b %EXIT_CODE%
|
|
||||||
|
|
||||||
:mainEnd
|
:mainEnd
|
||||||
if "%OS%"=="Windows_NT" endlocal
|
if "%OS%"=="Windows_NT" endlocal
|
|
@ -0,0 +1,5 @@
|
||||||
|
plugins {
|
||||||
|
id("org.gradle.toolchains.foojay-resolver-convention") version "0.5.0"
|
||||||
|
}
|
||||||
|
rootProject.name = "hideout-activitypub"
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
package dev.usbharu
|
||||||
|
|
||||||
|
fun main() {
|
||||||
|
println("Hello World!")
|
||||||
|
}
|
|
@ -3,15 +3,13 @@ import com.github.jk1.license.filter.LicenseBundleNormalizer
|
||||||
import com.github.jk1.license.importer.DependencyDataImporter
|
import com.github.jk1.license.importer.DependencyDataImporter
|
||||||
import com.github.jk1.license.importer.XmlReportImporter
|
import com.github.jk1.license.importer.XmlReportImporter
|
||||||
import com.github.jk1.license.render.*
|
import com.github.jk1.license.render.*
|
||||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||||
import org.openapitools.generator.gradle.plugin.tasks.GenerateTask
|
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
alias(libs.plugins.kotlin.jvm)
|
alias(libs.plugins.kotlin.jvm)
|
||||||
alias(libs.plugins.detekt)
|
alias(libs.plugins.detekt)
|
||||||
alias(libs.plugins.spring.boot)
|
alias(libs.plugins.spring.boot)
|
||||||
alias(libs.plugins.kotlin.spring)
|
alias(libs.plugins.kotlin.spring)
|
||||||
alias(libs.plugins.openapi.generator)
|
|
||||||
alias(libs.plugins.kover)
|
alias(libs.plugins.kover)
|
||||||
alias(libs.plugins.license.report)
|
alias(libs.plugins.license.report)
|
||||||
|
|
||||||
|
@ -24,58 +22,6 @@ apply {
|
||||||
group = "dev.usbharu"
|
group = "dev.usbharu"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
|
|
||||||
sourceSets {
|
|
||||||
create("intTest") {
|
|
||||||
compileClasspath += sourceSets.main.get().output
|
|
||||||
runtimeClasspath += sourceSets.main.get().output
|
|
||||||
}
|
|
||||||
create("e2eTest") {
|
|
||||||
compileClasspath += sourceSets.main.get().output
|
|
||||||
runtimeClasspath += sourceSets.main.get().output
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val intTestImplementation by configurations.getting {
|
|
||||||
extendsFrom(configurations.implementation.get())
|
|
||||||
}
|
|
||||||
val intTestRuntimeOnly by configurations.getting {
|
|
||||||
extendsFrom(configurations.runtimeOnly.get())
|
|
||||||
}
|
|
||||||
|
|
||||||
val e2eTestImplementation by configurations.getting {
|
|
||||||
extendsFrom(configurations.implementation.get())
|
|
||||||
}
|
|
||||||
|
|
||||||
val e2eTestRuntimeOnly by configurations.getting {
|
|
||||||
extendsFrom(configurations.runtimeOnly.get())
|
|
||||||
}
|
|
||||||
|
|
||||||
val integrationTest = task<Test>("integrationTest") {
|
|
||||||
description = "Runs integration tests."
|
|
||||||
group = "verification"
|
|
||||||
|
|
||||||
testClassesDirs = sourceSets["intTest"].output.classesDirs
|
|
||||||
classpath = sourceSets["intTest"].runtimeClasspath
|
|
||||||
shouldRunAfter("test")
|
|
||||||
|
|
||||||
useJUnitPlatform()
|
|
||||||
}
|
|
||||||
|
|
||||||
val e2eTest = task<Test>("e2eTest") {
|
|
||||||
description = "Runs e2e tests."
|
|
||||||
group = "verification"
|
|
||||||
|
|
||||||
testClassesDirs = sourceSets["e2eTest"].output.classesDirs
|
|
||||||
classpath = sourceSets["e2eTest"].runtimeClasspath
|
|
||||||
shouldRunAfter("test")
|
|
||||||
|
|
||||||
useJUnitPlatform()
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.check {
|
|
||||||
dependsOn(integrationTest)
|
|
||||||
dependsOn(e2eTest)
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.withType<Test> {
|
tasks.withType<Test> {
|
||||||
useJUnitPlatform()
|
useJUnitPlatform()
|
||||||
|
@ -84,44 +30,20 @@ tasks.withType<Test> {
|
||||||
"--add-opens", "java.base/java.lang=ALL-UNNAMED",
|
"--add-opens", "java.base/java.lang=ALL-UNNAMED",
|
||||||
"--add-opens", "java.base/java.util=ALL-UNNAMED",
|
"--add-opens", "java.base/java.util=ALL-UNNAMED",
|
||||||
"--add-opens", "java.naming/javax.naming=ALL-UNNAMED",
|
"--add-opens", "java.naming/javax.naming=ALL-UNNAMED",
|
||||||
|
"--add-opens", "java.base/java.util.concurrent.locks=ALL-UNNAMED"
|
||||||
).toMutableList()
|
).toMutableList()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
kotlin {
|
||||||
tasks.withType<KotlinCompile> {
|
jvmToolchain(21)
|
||||||
kotlinOptions {
|
compilerOptions {
|
||||||
freeCompilerArgs += "-Xjsr305=strict"
|
freeCompilerArgs.add("-Xjsr305=strict")
|
||||||
|
jvmTarget = JvmTarget.JVM_21
|
||||||
}
|
}
|
||||||
dependsOn("openApiGenerateMastodonCompatibleApi")
|
|
||||||
mustRunAfter("openApiGenerateMastodonCompatibleApi")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
tasks.clean {
|
|
||||||
delete += listOf("$rootDir/src/main/resources/static")
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.create<GenerateTask>("openApiGenerateMastodonCompatibleApi", GenerateTask::class) {
|
|
||||||
generatorName.set("kotlin-spring")
|
|
||||||
inputSpec.set("$rootDir/src/main/resources/openapi/mastodon.yaml")
|
|
||||||
outputDir.set("$buildDir/generated/sources/mastodon")
|
|
||||||
apiPackage.set("dev.usbharu.hideout.controller.mastodon.generated")
|
|
||||||
modelPackage.set("dev.usbharu.hideout.domain.mastodon.model.generated")
|
|
||||||
configOptions.put("interfaceOnly", "true")
|
|
||||||
configOptions.put("useSpringBoot3", "true")
|
|
||||||
configOptions.put("reactive", "true")
|
|
||||||
additionalProperties.put("useTags", "true")
|
|
||||||
|
|
||||||
importMappings.put("org.springframework.core.io.Resource", "org.springframework.web.multipart.MultipartFile")
|
|
||||||
typeMappings.put("org.springframework.core.io.Resource", "org.springframework.web.multipart.MultipartFile")
|
|
||||||
schemaMappings.put(
|
|
||||||
"StatusesRequest",
|
|
||||||
"dev.usbharu.hideout.mastodon.interfaces.api.status.StatusesRequest"
|
|
||||||
)
|
|
||||||
templateDir.set("$rootDir/templates")
|
|
||||||
}
|
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
maven {
|
maven {
|
||||||
|
@ -147,21 +69,6 @@ repositories {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
kotlin {
|
|
||||||
target {
|
|
||||||
compilations.all {
|
|
||||||
kotlinOptions.jvmTarget = JavaVersion.VERSION_21.toString()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sourceSets.main {
|
|
||||||
kotlin.srcDirs(
|
|
||||||
"$buildDir/generated/ksp/main",
|
|
||||||
"$buildDir/generated/sources/openapi/src/main/kotlin",
|
|
||||||
"$buildDir/generated/sources/mastodon/src/main/kotlin"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
val os = org.gradle.nativeplatform.platform.internal
|
val os = org.gradle.nativeplatform.platform.internal
|
||||||
.DefaultNativePlatform.getCurrentOperatingSystem()
|
.DefaultNativePlatform.getCurrentOperatingSystem()
|
||||||
|
@ -187,6 +94,7 @@ dependencies {
|
||||||
implementation("org.springframework.boot:spring-boot-starter-log4j2")
|
implementation("org.springframework.boot:spring-boot-starter-log4j2")
|
||||||
implementation("org.springframework.boot:spring-boot-starter-validation")
|
implementation("org.springframework.boot:spring-boot-starter-validation")
|
||||||
|
|
||||||
|
|
||||||
implementation(libs.blurhash)
|
implementation(libs.blurhash)
|
||||||
implementation(libs.aws.s3)
|
implementation(libs.aws.s3)
|
||||||
implementation(libs.jsoup)
|
implementation(libs.jsoup)
|
||||||
|
@ -195,6 +103,7 @@ dependencies {
|
||||||
implementation(libs.imageio.webp)
|
implementation(libs.imageio.webp)
|
||||||
implementation(libs.thumbnailator)
|
implementation(libs.thumbnailator)
|
||||||
implementation(libs.flyway.core)
|
implementation(libs.flyway.core)
|
||||||
|
runtimeOnly(libs.flyway.postgresql)
|
||||||
|
|
||||||
implementation("dev.usbharu:owl-common-serialize-jackson:0.0.1")
|
implementation("dev.usbharu:owl-common-serialize-jackson:0.0.1")
|
||||||
|
|
||||||
|
@ -221,30 +130,15 @@ dependencies {
|
||||||
|
|
||||||
|
|
||||||
testImplementation("org.springframework.boot:spring-boot-starter-test")
|
testImplementation("org.springframework.boot:spring-boot-starter-test")
|
||||||
implementation(libs.kotlin.junit)
|
testImplementation(libs.kotlin.junit)
|
||||||
implementation(libs.coroutines.test)
|
testImplementation(libs.coroutines.test)
|
||||||
|
|
||||||
testImplementation(libs.ktor.client.mock)
|
testImplementation(libs.ktor.client.mock)
|
||||||
testImplementation(libs.h2db)
|
testImplementation(libs.h2db)
|
||||||
|
|
||||||
testImplementation("org.mockito.kotlin:mockito-kotlin:5.3.1")
|
testImplementation("org.mockito.kotlin:mockito-kotlin:5.3.1")
|
||||||
testImplementation("org.mockito:mockito-inline:5.2.0")
|
testImplementation("org.mockito:mockito-inline:5.2.0")
|
||||||
testImplementation("nl.jqno.equalsverifier:equalsverifier:3.16.1")
|
testImplementation("nl.jqno.equalsverifier:equalsverifier:3.16.1")
|
||||||
testImplementation("com.jparams:to-string-verifier:1.4.8")
|
testImplementation("com.jparams:to-string-verifier:1.4.8")
|
||||||
|
|
||||||
intTestImplementation("org.springframework.boot:spring-boot-starter-test")
|
|
||||||
intTestImplementation("org.springframework.security:spring-security-test")
|
|
||||||
intTestImplementation(libs.kotlin.junit)
|
|
||||||
intTestImplementation(libs.coroutines.test)
|
|
||||||
intTestImplementation("org.mockito.kotlin:mockito-kotlin:5.3.1")
|
|
||||||
intTestImplementation(libs.h2db)
|
|
||||||
|
|
||||||
e2eTestImplementation("org.springframework.boot:spring-boot-starter-test")
|
|
||||||
e2eTestImplementation("org.springframework.security:spring-security-test")
|
|
||||||
e2eTestImplementation("org.springframework.boot:spring-boot-starter-webflux")
|
|
||||||
e2eTestImplementation("com.intuit.karate:karate-junit5:1.4.1")
|
|
||||||
e2eTestImplementation(libs.h2db)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
detekt {
|
detekt {
|
||||||
|
@ -332,7 +226,9 @@ kover {
|
||||||
}
|
}
|
||||||
|
|
||||||
springBoot {
|
springBoot {
|
||||||
buildInfo()
|
buildInfo {
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
licenseReport {
|
licenseReport {
|
||||||
|
|
|
@ -18,3 +18,5 @@ org.gradle.parallel=true
|
||||||
org.gradle.configureondemand=true
|
org.gradle.configureondemand=true
|
||||||
org.gradle.caching=true
|
org.gradle.caching=true
|
||||||
org.gradle.jvmargs=-Xmx4096m -XX:+HeapDumpOnOutOfMemoryError -XX:+UseParallelGC
|
org.gradle.jvmargs=-Xmx4096m -XX:+HeapDumpOnOutOfMemoryError -XX:+UseParallelGC
|
||||||
|
org.gradle.configuration-cache=true
|
||||||
|
org.gradle.configuration-cache.problems=warn
|
|
@ -1,43 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.core.infrastructure.exposedrepository.Actors
|
|
||||||
import org.jetbrains.exposed.sql.and
|
|
||||||
import org.jetbrains.exposed.sql.selectAll
|
|
||||||
import java.net.MalformedURLException
|
|
||||||
import java.net.URL
|
|
||||||
|
|
||||||
object AssertionUtil {
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
fun assertUserExist(username: String, domain: String): Boolean {
|
|
||||||
val s = try {
|
|
||||||
val url = URL(domain)
|
|
||||||
url.host + ":" + url.port.toString().takeIf { it != "-1" }.orEmpty()
|
|
||||||
} catch (e: MalformedURLException) {
|
|
||||||
domain
|
|
||||||
}
|
|
||||||
|
|
||||||
val selectAll = Actors.selectAll()
|
|
||||||
println(selectAll.fetchSize)
|
|
||||||
|
|
||||||
println(selectAll.toList().size)
|
|
||||||
|
|
||||||
selectAll.map { "@${it[Actors.name]}@${it[Actors.domain]}" }.forEach { println(it) }
|
|
||||||
|
|
||||||
return Actors.selectAll().where { Actors.name eq username and (Actors.domain eq s) }.empty().not()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,44 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import com.intuit.karate.junit5.Karate
|
|
||||||
|
|
||||||
object KarateUtil {
|
|
||||||
fun springBootKarateTest(path: String, scenario: String, clazz: Class<*>, port: String): Karate {
|
|
||||||
if (scenario.isEmpty()) {
|
|
||||||
return Karate.run(path).relativeTo(clazz).systemProperty("karate.port", port).karateEnv("dev")
|
|
||||||
} else {
|
|
||||||
return Karate.run(path).scenarioName(scenario).relativeTo(clazz).systemProperty("karate.port", port)
|
|
||||||
.karateEnv("dev")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun e2eTest(path: String, scenario: String = "", properties: Map<String, String>, clazz: Class<*>): Karate {
|
|
||||||
val run = Karate.run(path)
|
|
||||||
|
|
||||||
val karate = if (scenario.isEmpty()) {
|
|
||||||
run
|
|
||||||
} else {
|
|
||||||
run.scenarioName(scenario)
|
|
||||||
}
|
|
||||||
|
|
||||||
var relativeTo = karate.relativeTo(clazz)
|
|
||||||
|
|
||||||
properties.map { relativeTo = relativeTo.systemProperty(it.key, it.value) }
|
|
||||||
|
|
||||||
return relativeTo.karateEnv("dev")
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,101 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package federation
|
|
||||||
|
|
||||||
import AssertionUtil
|
|
||||||
import KarateUtil
|
|
||||||
import com.intuit.karate.core.MockServer
|
|
||||||
import com.intuit.karate.junit5.Karate
|
|
||||||
import dev.usbharu.hideout.SpringApplication
|
|
||||||
import kotlinx.coroutines.delay
|
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import org.flywaydb.core.Flyway
|
|
||||||
import org.junit.jupiter.api.*
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired
|
|
||||||
import org.springframework.boot.test.context.SpringBootTest
|
|
||||||
import org.springframework.boot.test.web.server.LocalServerPort
|
|
||||||
import org.springframework.transaction.annotation.Transactional
|
|
||||||
import java.net.MalformedURLException
|
|
||||||
import java.net.URL
|
|
||||||
|
|
||||||
@SpringBootTest(
|
|
||||||
classes = [SpringApplication::class],
|
|
||||||
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT
|
|
||||||
)
|
|
||||||
@Transactional
|
|
||||||
class FollowAcceptTest {
|
|
||||||
@LocalServerPort
|
|
||||||
private var port = ""
|
|
||||||
|
|
||||||
@Karate.Test
|
|
||||||
@TestFactory
|
|
||||||
@Disabled
|
|
||||||
fun `FollowAcceptTest`(): Karate {
|
|
||||||
return KarateUtil.e2eTest(
|
|
||||||
"FollowAcceptTest", "Follow Accept Test",
|
|
||||||
mapOf("karate.port" to port),
|
|
||||||
javaClass
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
lateinit var server: MockServer
|
|
||||||
|
|
||||||
lateinit var _remotePort: String
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
fun assertUserExist(username: String, domain: String) = runBlocking {
|
|
||||||
val s = try {
|
|
||||||
val url = URL(domain)
|
|
||||||
url.host + ":" + url.port.toString().takeIf { it != "-1" }.orEmpty()
|
|
||||||
} catch (e: MalformedURLException) {
|
|
||||||
domain
|
|
||||||
}
|
|
||||||
|
|
||||||
var check = false
|
|
||||||
|
|
||||||
repeat(10) {
|
|
||||||
delay(1000)
|
|
||||||
check = AssertionUtil.assertUserExist(username, s) or check
|
|
||||||
if (check) {
|
|
||||||
return@repeat
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Assertions.assertTrue(check, "User @$username@$s not exist.")
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
fun getRemotePort(): String = _remotePort
|
|
||||||
|
|
||||||
@BeforeAll
|
|
||||||
@JvmStatic
|
|
||||||
fun beforeAll(@Autowired flyway: Flyway) {
|
|
||||||
server = MockServer.feature("classpath:federation/FollowAcceptMockServer.feature").http(0).build()
|
|
||||||
_remotePort = server.port.toString()
|
|
||||||
|
|
||||||
flyway.clean()
|
|
||||||
flyway.migrate()
|
|
||||||
}
|
|
||||||
|
|
||||||
@AfterAll
|
|
||||||
@JvmStatic
|
|
||||||
fun afterAll() {
|
|
||||||
server.stop()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,146 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package federation
|
|
||||||
|
|
||||||
import AssertionUtil
|
|
||||||
import KarateUtil
|
|
||||||
import com.intuit.karate.core.MockServer
|
|
||||||
import com.intuit.karate.junit5.Karate
|
|
||||||
import dev.usbharu.hideout.SpringApplication
|
|
||||||
import kotlinx.coroutines.delay
|
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import org.flywaydb.core.Flyway
|
|
||||||
import org.junit.jupiter.api.AfterAll
|
|
||||||
import org.junit.jupiter.api.Assertions.assertTrue
|
|
||||||
import org.junit.jupiter.api.BeforeAll
|
|
||||||
import org.junit.jupiter.api.Disabled
|
|
||||||
import org.junit.jupiter.api.TestFactory
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired
|
|
||||||
import org.springframework.boot.test.context.SpringBootTest
|
|
||||||
import org.springframework.boot.test.web.server.LocalServerPort
|
|
||||||
import org.springframework.transaction.annotation.Transactional
|
|
||||||
|
|
||||||
@SpringBootTest(
|
|
||||||
classes = [SpringApplication::class],
|
|
||||||
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT
|
|
||||||
)
|
|
||||||
@Transactional
|
|
||||||
@Disabled
|
|
||||||
class InboxCommonTest {
|
|
||||||
@LocalServerPort
|
|
||||||
private var port = ""
|
|
||||||
|
|
||||||
@Karate.Test
|
|
||||||
@TestFactory
|
|
||||||
fun `inboxにHTTP Signature付きのリクエストがあったらリモートに取得しに行く`(): Karate {
|
|
||||||
return KarateUtil.e2eTest(
|
|
||||||
"InboxCommonTest",
|
|
||||||
"inboxにHTTP Signature付きのリクエストがあったらリモートに取得しに行く",
|
|
||||||
mapOf(
|
|
||||||
"karate.port" to port,
|
|
||||||
"karate.remotePort" to _remotePort
|
|
||||||
),
|
|
||||||
javaClass
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Karate.Test
|
|
||||||
@TestFactory
|
|
||||||
fun `user-inboxにHTTP Signature付きのリクエストがあったらリモートに取得しに行く`(): Karate {
|
|
||||||
return KarateUtil.e2eTest(
|
|
||||||
"InboxCommonTest",
|
|
||||||
"user-inboxにHTTP Signature付きのリクエストがあったらリモートに取得しに行く",
|
|
||||||
mapOf(
|
|
||||||
"karate.port" to port,
|
|
||||||
"karate.remotePort" to _remotePort
|
|
||||||
),
|
|
||||||
javaClass
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Karate.Test
|
|
||||||
@TestFactory
|
|
||||||
fun `inboxにHTTP Signatureがないリクエストがきたら401を返す`(): Karate {
|
|
||||||
return KarateUtil.e2eTest(
|
|
||||||
"InboxCommonTest",
|
|
||||||
"inboxにHTTP Signatureがないリクエストがきたら401を返す",
|
|
||||||
mapOf("karate.port" to port),
|
|
||||||
javaClass
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Karate.Test
|
|
||||||
@TestFactory
|
|
||||||
fun `user-inboxにHTTP Signatureがないリクエストがきたら401を返す`(): Karate {
|
|
||||||
return KarateUtil.e2eTest(
|
|
||||||
"InboxCommonTest",
|
|
||||||
"user-inboxにHTTP Signatureがないリクエストがきたら401を返す",
|
|
||||||
mapOf("karate.port" to port),
|
|
||||||
javaClass
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Karate.Test
|
|
||||||
@TestFactory
|
|
||||||
fun `inboxにConetnt-Type application *+json以外が来たら415を返す`(): Karate {
|
|
||||||
return KarateUtil.e2eTest(
|
|
||||||
"InboxCommonTest",
|
|
||||||
"inboxにContent-Type application/json以外が来たら415を返す",
|
|
||||||
mapOf("karate.port" to port),
|
|
||||||
javaClass
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
lateinit var server: MockServer
|
|
||||||
|
|
||||||
lateinit var _remotePort: String
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
fun assertUserExist(username: String, domain: String) = runBlocking {
|
|
||||||
var check = false
|
|
||||||
|
|
||||||
repeat(10) {
|
|
||||||
delay(1000)
|
|
||||||
check = AssertionUtil.assertUserExist(username, domain) or check
|
|
||||||
if (check) {
|
|
||||||
return@repeat
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
assertTrue(check, "User @$username@$domain not exist.")
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
fun getRemotePort(): String = _remotePort
|
|
||||||
|
|
||||||
@BeforeAll
|
|
||||||
@JvmStatic
|
|
||||||
fun beforeAll() {
|
|
||||||
server = MockServer.feature("classpath:federation/InboxxCommonMockServerTest.feature").http(0).build()
|
|
||||||
_remotePort = server.port.toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
@AfterAll
|
|
||||||
@JvmStatic
|
|
||||||
fun afterAll(@Autowired flyway: Flyway) {
|
|
||||||
server.stop()
|
|
||||||
flyway.clean()
|
|
||||||
flyway.migrate()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,70 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package oauth2
|
|
||||||
|
|
||||||
import KarateUtil
|
|
||||||
import com.intuit.karate.junit5.Karate
|
|
||||||
import dev.usbharu.hideout.SpringApplication
|
|
||||||
import org.flywaydb.core.Flyway
|
|
||||||
import org.junit.jupiter.api.AfterAll
|
|
||||||
import org.junit.jupiter.api.TestFactory
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired
|
|
||||||
import org.springframework.boot.test.context.SpringBootTest
|
|
||||||
import org.springframework.boot.test.web.server.LocalServerPort
|
|
||||||
import org.springframework.test.context.jdbc.Sql
|
|
||||||
|
|
||||||
@SpringBootTest(
|
|
||||||
classes = [SpringApplication::class],
|
|
||||||
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
|
||||||
)
|
|
||||||
@Sql("/oauth2/user.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_CLASS)
|
|
||||||
class OAuth2LoginTest {
|
|
||||||
|
|
||||||
@LocalServerPort
|
|
||||||
private var port = ""
|
|
||||||
|
|
||||||
@Karate.Test
|
|
||||||
@TestFactory
|
|
||||||
fun `スコープwrite readを持ったトークンの作成`(): Karate {
|
|
||||||
return KarateUtil.springBootKarateTest(
|
|
||||||
"Oauth2LoginTest",
|
|
||||||
"スコープwrite readを持ったトークンの作成",
|
|
||||||
javaClass,
|
|
||||||
port
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Karate.Test
|
|
||||||
@TestFactory
|
|
||||||
fun `スコープread_statuses write_statusesを持ったトークンの作成`(): Karate {
|
|
||||||
return KarateUtil.springBootKarateTest(
|
|
||||||
"Oauth2LoginTest",
|
|
||||||
"スコープread:statuses write:statusesを持ったトークンの作成",
|
|
||||||
javaClass,
|
|
||||||
port
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
@JvmStatic
|
|
||||||
@AfterAll
|
|
||||||
fun dropDatabase(@Autowired flyway: Flyway) {
|
|
||||||
flyway.clean()
|
|
||||||
flyway.migrate()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,44 +0,0 @@
|
||||||
hideout:
|
|
||||||
url: "https://localhost:8080"
|
|
||||||
use-mongodb: false
|
|
||||||
security:
|
|
||||||
jwt:
|
|
||||||
generate: true
|
|
||||||
key-id: a
|
|
||||||
private-key: "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC7VJTUt9Us8cKjMzEfYyjiWA4R4/M2bS1GB4t7NXp98C3SC6dVMvDuictGeurT8jNbvJZHtCSuYEvuNMoSfm76oqFvAp8Gy0iz5sxjZmSnXyCdPEovGhLa0VzMaQ8s+CLOyS56YyCFGeJZqgtzJ6GR3eqoYSW9b9UMvkBpZODSctWSNGj3P7jRFDO5VoTwCQAWbFnOjDfH5Ulgp2PKSQnSJP3AJLQNFNe7br1XbrhV//eO+t51mIpGSDCUv3E0DDFcWDTH9cXDTTlRZVEiR2BwpZOOkE/Z0/BVnhZYL71oZV34bKfWjQIt6V/isSMahdsAASACp4ZTGtwiVuNd9tybAgMBAAECggEBAKTmjaS6tkK8BlPXClTQ2vpz/N6uxDeS35mXpqasqskVlaAidgg/sWqpjXDbXr93otIMLlWsM+X0CqMDgSXKejLS2jx4GDjI1ZTXg++0AMJ8sJ74pWzVDOfmCEQ/7wXs3+cbnXhKriO8Z036q92Qc1+N87SI38nkGa0ABH9CN83HmQqt4fB7UdHzuIRe/me2PGhIq5ZBzj6h3BpoPGzEP+x3l9YmK8t/1cN0pqI+dQwYdgfGjackLu/2qH80MCF7IyQaseZUOJyKrCLtSD/Iixv/hzDEUPfOCjFDgTpzf3cwta8+oE4wHCo1iI1/4TlPkwmXx4qSXtmw4aQPz7IDQvECgYEA8KNThCO2gsC2I9PQDM/8Cw0O983WCDY+oi+7JPiNAJwv5DYBqEZB1QYdj06YD16XlC/HAZMsMku1na2TN0driwenQQWzoev3g2S7gRDoS/FCJSI3jJ+kjgtaA7Qmzlgk1TxODN+G1H91HW7t0l7VnL27IWyYo2qRRK3jzxqUiPUCgYEAx0oQs2reBQGMVZnApD1jeq7n4MvNLcPvt8b/eU9iUv6Y4Mj0Suo/AU8lYZXm8ubbqAlwz2VSVunD2tOplHyMUrtCtObAfVDUAhCndKaA9gApgfb3xw1IKbuQ1u4IF1FJl3VtumfQn//LiH1B3rXhcdyo3/vIttEk48RakUKClU8CgYEAzV7W3COOlDDcQd935DdtKBFRAPRPAlspQUnzMi5eSHMD/ISLDY5IiQHbIH83D4bvXq0X7qQoSBSNP7Dvv3HYuqMhf0DaegrlBuJllFVVq9qPVRnKxt1Il2HgxOBvbhOT+9in1BzA+YJ99UzC85O0Qz06A+CmtHEy4aZ2kj5hHjECgYEAmNS4+A8Fkss8Js1RieK2LniBxMgmYml3pfVLKGnzmng7H2+cwPLhPIzIuwytXywh2bzbsYEfYx3EoEVgMEpPhoarQnYPukrJO4gwE2o5Te6T5mJSZGlQJQj9q4ZB2Dfzet6INsK0oG8XVGXSpQvQh3RUYekCZQkBBFcpqWpbIEsCgYAnM3DQf3FJoSnXaMhrVBIovic5l0xFkEHskAjFTevO86Fsz1C2aSeRKSqGFoOQ0tmJzBEs1R6KqnHInicDTQrKhArgLXX4v3CddjfTRJkFWDbE/CkvKZNOrcf1nhaGCPspRJj2KUkj1Fhl9Cncdn/RsYEONbwQSjIfMPkvxF+8HQ=="
|
|
||||||
public-key: "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu1SU1LfVLPHCozMxH2Mo4lgOEePzNm0tRgeLezV6ffAt0gunVTLw7onLRnrq0/IzW7yWR7QkrmBL7jTKEn5u+qKhbwKfBstIs+bMY2Zkp18gnTxKLxoS2tFczGkPLPgizskuemMghRniWaoLcyehkd3qqGElvW/VDL5AaWTg0nLVkjRo9z+40RQzuVaE8AkAFmxZzow3x+VJYKdjykkJ0iT9wCS0DRTXu269V264Vf/3jvredZiKRkgwlL9xNAwxXFg0x/XFw005UWVRIkdgcKWTjpBP2dPwVZ4WWC+9aGVd+Gyn1o0CLelf4rEjGoXbAAEgAqeGUxrcIlbjXfbcmwIDAQAB"
|
|
||||||
storage:
|
|
||||||
type: local
|
|
||||||
debug:
|
|
||||||
trace-query-exception: true
|
|
||||||
trace-query-call: true
|
|
||||||
private: false
|
|
||||||
|
|
||||||
spring:
|
|
||||||
flyway:
|
|
||||||
enabled: true
|
|
||||||
clean-disabled: false
|
|
||||||
datasource:
|
|
||||||
driver-class-name: org.h2.Driver
|
|
||||||
url: "jdbc:h2:mem:e2e-test;MODE=POSTGRESQL;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE;CASE_INSENSITIVE_IDENTIFIERS=true;TRACE_LEVEL_FILE=4"
|
|
||||||
username: ""
|
|
||||||
password:
|
|
||||||
data:
|
|
||||||
mongodb:
|
|
||||||
auto-index-creation: true
|
|
||||||
host: localhost
|
|
||||||
port: 27017
|
|
||||||
database: hideout
|
|
||||||
h2:
|
|
||||||
console:
|
|
||||||
enabled: true
|
|
||||||
|
|
||||||
# exposed:
|
|
||||||
# generate-ddl: true
|
|
||||||
# excluded-packages: dev.usbharu.hideout.core.infrastructure.kjobexposed
|
|
||||||
server:
|
|
||||||
port: 8080
|
|
||||||
tomcat:
|
|
||||||
basedir: tomcat-e2e
|
|
||||||
accesslog:
|
|
||||||
enabled: true
|
|
|
@ -1,140 +0,0 @@
|
||||||
Feature: Follow Accept Mock Server
|
|
||||||
|
|
||||||
Background:
|
|
||||||
* def assertInbox = Java.type(`federation.FollowAcceptTest`)
|
|
||||||
* def req = {req: []}
|
|
||||||
|
|
||||||
Scenario: pathMatches('/users/test-follower') && methodIs('get')
|
|
||||||
* def remoteUrl = 'http://localhost:' + assertInbox.getRemotePort()
|
|
||||||
* def username = 'test-follower'
|
|
||||||
* def userUrl = remoteUrl + '/users/' + username
|
|
||||||
|
|
||||||
|
|
||||||
* def person =
|
|
||||||
"""
|
|
||||||
{
|
|
||||||
"@context": [
|
|
||||||
"https://www.w3.org/ns/activitystreams",
|
|
||||||
"https://w3id.org/security/v1",
|
|
||||||
{
|
|
||||||
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
|
|
||||||
"toot": "http://joinmastodon.org/ns#",
|
|
||||||
"featured": {
|
|
||||||
"@id": "toot:featured",
|
|
||||||
"@type": "@id"
|
|
||||||
},
|
|
||||||
"featuredTags": {
|
|
||||||
"@id": "toot:featuredTags",
|
|
||||||
"@type": "@id"
|
|
||||||
},
|
|
||||||
"alsoKnownAs": {
|
|
||||||
"@id": "as:alsoKnownAs",
|
|
||||||
"@type": "@id"
|
|
||||||
},
|
|
||||||
"movedTo": {
|
|
||||||
"@id": "as:movedTo",
|
|
||||||
"@type": "@id"
|
|
||||||
},
|
|
||||||
"schema": "http://schema.org#",
|
|
||||||
"PropertyValue": "schema:PropertyValue",
|
|
||||||
"value": "schema:value",
|
|
||||||
"discoverable": "toot:discoverable",
|
|
||||||
"Device": "toot:Device",
|
|
||||||
"Ed25519Signature": "toot:Ed25519Signature",
|
|
||||||
"Ed25519Key": "toot:Ed25519Key",
|
|
||||||
"Curve25519Key": "toot:Curve25519Key",
|
|
||||||
"EncryptedMessage": "toot:EncryptedMessage",
|
|
||||||
"publicKeyBase64": "toot:publicKeyBase64",
|
|
||||||
"deviceId": "toot:deviceId",
|
|
||||||
"claim": {
|
|
||||||
"@type": "@id",
|
|
||||||
"@id": "toot:claim"
|
|
||||||
},
|
|
||||||
"fingerprintKey": {
|
|
||||||
"@type": "@id",
|
|
||||||
"@id": "toot:fingerprintKey"
|
|
||||||
},
|
|
||||||
"identityKey": {
|
|
||||||
"@type": "@id",
|
|
||||||
"@id": "toot:identityKey"
|
|
||||||
},
|
|
||||||
"devices": {
|
|
||||||
"@type": "@id",
|
|
||||||
"@id": "toot:devices"
|
|
||||||
},
|
|
||||||
"messageFranking": "toot:messageFranking",
|
|
||||||
"messageType": "toot:messageType",
|
|
||||||
"cipherText": "toot:cipherText",
|
|
||||||
"suspended": "toot:suspended",
|
|
||||||
"focalPoint": {
|
|
||||||
"@container": "@list",
|
|
||||||
"@id": "toot:focalPoint"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"id": #(userUrl),
|
|
||||||
"type": "Person",
|
|
||||||
"following": #(userUrl + '/following'),
|
|
||||||
"followers": #(userUrl + '/followers'),
|
|
||||||
"inbox": #(userUrl + '/inbox'),
|
|
||||||
"outbox": #(userUrl + '/outbox'),
|
|
||||||
"featured": #(userUrl + '/collections/featured'),
|
|
||||||
"featuredTags": #(userUrl + '/collections/tags'),
|
|
||||||
"preferredUsername": #(username),
|
|
||||||
"name": #(username),
|
|
||||||
"summary": "E2E Test User Jaga/Cotlin/Winter Boot/Ktol\nYonTude: https://example.com\nY(Tvvitter): https://example.com\n",
|
|
||||||
"url": #(userUrl + '/@' + username),
|
|
||||||
"manuallyApprovesFollowers": false,
|
|
||||||
"discoverable": true,
|
|
||||||
"published": "2016-03-16T00:00:00Z",
|
|
||||||
"devices": #(userUrl + '/collections/devices'),
|
|
||||||
"alsoKnownAs": [
|
|
||||||
#( 'https://example.com/users/' + username)
|
|
||||||
],
|
|
||||||
"publicKey": {
|
|
||||||
"id": #(userUrl + '#main-key'),
|
|
||||||
"owner": #(userUrl),
|
|
||||||
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvmtKo0xYGXR4M0LQQhK4\nBkKpRvvUxrqGV6Ew4CBHSyzdnbFsiBqHUWz4JvRiQvAPqQ4jQFpxVZCPr9xx6lJp\nx7EAAKIdVTnBBV4CYfu7QPsRqtjbB5408q+mo5oUXNs8xg2tcC42p2SJ5CRJX/fr\nOgCZwo3LC9pOBdCQZ+tiiPmWNBTNby99JZn4D/xNcwuhV04qcPoHYD9OPuxxGyzc\naVJ2mqJmvi/lewQuR8qnUIbz+Gik+xvyG6LmyuDoa1H2LDQfQXYb62G70HsYdu7a\ndObvZovytp+kkjP/cUaIYkhhOAYqAA4zCwVRY4NHK0MAMq9sMoUfNJa8U+zR9NvD\noQIDAQAB\n-----END PUBLIC KEY-----\n"
|
|
||||||
},
|
|
||||||
"tag": [],
|
|
||||||
"attachment": [
|
|
||||||
{
|
|
||||||
"type": "PropertyValue",
|
|
||||||
"name": "Pixib Fan-Bridge",
|
|
||||||
"value": "\u003ca href=\"https://example.com/hideout\" target=\"_blank\" rel=\"nofollow noopener noreferrer me\"\u003e\u003cspan class=\"invisible\"\u003ehttps://www.\u003c/span\u003e\u003cspan class=\"\"\u003eexample.com/hideout\u003c/span\u003e\u003cspan class=\"invisible\"\u003e\u003c/span\u003e\u003c/a\u003e"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "PropertyValue",
|
|
||||||
"name": "GitHub",
|
|
||||||
"value": "\u003ca href=\"https://github.com/usbharu/hideout\" target=\"_blank\" rel=\"nofollow noopener noreferrer me\"\u003e\u003cspan class=\"invisible\"\u003ehttps://\u003c/span\u003e\u003cspan class=\"\"\u003egithub.com/usbharu/hideout\u003c/span\u003e\u003cspan class=\"invisible\"\u003e\u003c/span\u003e\u003c/a\u003e"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"endpoints": {
|
|
||||||
"sharedInbox": #(remoteUrl + '/inbox')
|
|
||||||
},
|
|
||||||
"icon": {
|
|
||||||
"type": "Image",
|
|
||||||
"mediaType": "image/jpeg",
|
|
||||||
"url": "https://upload.wikimedia.org/wikipedia/commons/thumb/3/30/Destroyer_Vozbuzhdenyy.jpg/320px-Destroyer_Vozbuzhdenyy.jpg"
|
|
||||||
},
|
|
||||||
"image": {
|
|
||||||
"type": "Image",
|
|
||||||
"mediaType": "image/jpeg",
|
|
||||||
"url": "https://upload.wikimedia.org/wikipedia/commons/thumb/6/63/Views_of_Mount_Fuji_from_%C5%8Cwakudani_20211202.jpg/320px-Views_of_Mount_Fuji_from_%C5%8Cwakudani_20211202.jpg"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
|
|
||||||
* set req.req[] = '/users/' + username
|
|
||||||
* def response = person
|
|
||||||
|
|
||||||
Scenario: pathMatches('/inbox') && methodIs('post')
|
|
||||||
* set req.req[] = '/inbox'
|
|
||||||
* def responseStatus = 202
|
|
||||||
|
|
||||||
Scenario: pathMatches('/internal-assertion-api/requests') && methodIs('get')
|
|
||||||
* def response = req
|
|
||||||
|
|
||||||
Scenario: pathMatches('/internal-assertion-api/requests/deleteAll') && methodIs('post')
|
|
||||||
* set req.req = []
|
|
||||||
* def responseStatus = 200
|
|
|
@ -1,29 +0,0 @@
|
||||||
Feature: Follow Accept Test
|
|
||||||
|
|
||||||
Background:
|
|
||||||
* url baseUrl
|
|
||||||
* def assertionUtil = Java.type('AssertionUtil')
|
|
||||||
|
|
||||||
Scenario: Follow Accept Test
|
|
||||||
|
|
||||||
* def follow =
|
|
||||||
"""
|
|
||||||
{"type": "Follow","actor": #(remoteUrl + '/users/test-follower'),"object": #(baseUrl + '/users/test-user')}
|
|
||||||
"""
|
|
||||||
|
|
||||||
Given path '/inbox'
|
|
||||||
And header Signature = 'keyId="https://test-hideout.usbharu.dev/users/c#pubkey", algorithm="rsa-sha256", headers="x-request-id tpp-redirect-uri digest psu-id", signature="e/91pFiI5bRffP33EMrqoI5A0xjkg3Ar0kzRGHC/1RsLrDW0zV50dHly/qJJ5xrYHRlss3+vd0mznTLBs1X0hx0uXjpfvCvwclpSi8u+sqn+Y2bcQKzf7ah0vAONQd6zeTYW7e/1kDJreP43PsJyz29KZD16Yop8nM++YeEs6C5eWiyYXKoQozXnfmTOX3y6bhxfKKQWVcxA5aLOTmTZRYTsBsTy9zn8NjDQaRI/0gcyYPqpq+2g8j2DbyJu3Z6zP6VmwbGGlQU/s9Pa7G5LqUPH/sBMSlIeqh+Hvm2pL7B3/BMFvGtTD+e2mR60BFnLIxMYx+oX4o33J2XkFIODLQ=="'
|
|
||||||
And request follow
|
|
||||||
When method post
|
|
||||||
Then status 202
|
|
||||||
|
|
||||||
And retry until assertionUtil.assertUserExist('test-follower',remoteUrl)
|
|
||||||
|
|
||||||
* url remoteUrl
|
|
||||||
|
|
||||||
Given path '/internal-assertion-api/requests'
|
|
||||||
When method get
|
|
||||||
Then status 200
|
|
||||||
And match response.req contains ['/users/test-follower']
|
|
||||||
|
|
||||||
* url baseUrl
|
|
|
@ -1,158 +0,0 @@
|
||||||
Feature: Inbox Common Test
|
|
||||||
|
|
||||||
Background:
|
|
||||||
* url baseUrl
|
|
||||||
|
|
||||||
Scenario: inboxにHTTP Signature付きのリクエストがあったらリモートに取得しに行く
|
|
||||||
|
|
||||||
* url remoteUrl
|
|
||||||
|
|
||||||
Given path '/internal-assertion-api/requests/deleteAll'
|
|
||||||
When method post
|
|
||||||
Then status 200
|
|
||||||
|
|
||||||
* url baseUrl
|
|
||||||
|
|
||||||
* def inbox =
|
|
||||||
"""
|
|
||||||
{ "type": "Follow" }
|
|
||||||
"""
|
|
||||||
|
|
||||||
Given path `/inbox`
|
|
||||||
And request inbox
|
|
||||||
# And header Signature = 'keyId="'+ remoteUrl +'/users/test-user#pubkey", algorithm="rsa-sha256", headers="(request-target)", signature="a"'
|
|
||||||
And header Signature = 'keyId="'+ remoteUrl +'/users/test-user#pubkey", algorithm="rsa-sha256", headers="(request-target) date host digest", signature="FfpkmBogW70FMo94yovGpl15L/m4bDjVIFb9mSZUstPE3H00nHiqNsjAq671qFMJsGOO1uWfLEExcdvzwTiC3wuHShzingvxQUbTgcgRTRZcHbtrOZxT8hYHGndpCXGv/NOLkfXDtZO9v5u0fnA2yJFokzyPHOPJ1cJliWlXP38Bl/pO4H5rBLQBZKpM2jYIjMyI78G2rDXNHEeGrGiyfB5SKb3H6zFQL+X9QpXUI4n0f07VsnwaDyp63oUopmzNUyBEuSqB+8va/lbfcWwrxpZnKGzQRZ+VBcV7jDoKGNOP9/O1xEI2CwB8sh+h6KVHdX3EQEvO1slaaLzcwRRqrQ=="'
|
|
||||||
When method post
|
|
||||||
Then status 202
|
|
||||||
|
|
||||||
* def assertInbox = Java.type(`federation.InboxCommonTest`)
|
|
||||||
|
|
||||||
And assertInbox.assertUserExist('test-user',remoteUrl)
|
|
||||||
|
|
||||||
* url remoteUrl
|
|
||||||
|
|
||||||
Given path '/internal-assertion-api/requests'
|
|
||||||
When method get
|
|
||||||
Then status 200
|
|
||||||
|
|
||||||
* url baseUrl
|
|
||||||
|
|
||||||
* print response
|
|
||||||
Then match response.req == ['/users/test-user']
|
|
||||||
|
|
||||||
|
|
||||||
Scenario: inboxにHTTP Signatureがないリクエストがきたら401を返す
|
|
||||||
|
|
||||||
* def inbox =
|
|
||||||
"""
|
|
||||||
{"type": "Follow"}
|
|
||||||
"""
|
|
||||||
|
|
||||||
Given path '/inbox'
|
|
||||||
And request inbox
|
|
||||||
When method post
|
|
||||||
Then status 401
|
|
||||||
|
|
||||||
|
|
||||||
Scenario: user-inboxにHTTP Signature付きのリクエストがあったらリモートに取得しに行く
|
|
||||||
|
|
||||||
* url remoteUrl
|
|
||||||
|
|
||||||
Given path '/internal-assertion-api/requests/deleteAll'
|
|
||||||
When method post
|
|
||||||
Then status 200
|
|
||||||
|
|
||||||
* url baseUrl
|
|
||||||
|
|
||||||
* def inbox =
|
|
||||||
"""
|
|
||||||
{ "type": "Follow" }
|
|
||||||
"""
|
|
||||||
|
|
||||||
Given path `/inbox`
|
|
||||||
And request inbox
|
|
||||||
# And header Signature = 'keyId="'+ remoteUrl +'/users/test-user#pubkey", algorithm="rsa-sha256", headers="(request-target)", signature="a"'
|
|
||||||
And header Signature = 'keyId="'+ remoteUrl +'/users/test-user2#pubkey", algorithm="rsa-sha256", headers="(request-target) date host digest", signature="FfpkmBogW70FMo94yovGpl15L/m4bDjVIFb9mSZUstPE3H00nHiqNsjAq671qFMJsGOO1uWfLEExcdvzwTiC3wuHShzingvxQUbTgcgRTRZcHbtrOZxT8hYHGndpCXGv/NOLkfXDtZO9v5u0fnA2yJFokzyPHOPJ1cJliWlXP38Bl/pO4H5rBLQBZKpM2jYIjMyI78G2rDXNHEeGrGiyfB5SKb3H6zFQL+X9QpXUI4n0f07VsnwaDyp63oUopmzNUyBEuSqB+8va/lbfcWwrxpZnKGzQRZ+VBcV7jDoKGNOP9/O1xEI2CwB8sh+h6KVHdX3EQEvO1slaaLzcwRRqrQ=="'
|
|
||||||
When method post
|
|
||||||
Then status 202
|
|
||||||
|
|
||||||
* def assertInbox = Java.type(`federation.InboxCommonTest`)
|
|
||||||
|
|
||||||
And assertInbox.assertUserExist('test-user2',remoteUrl)
|
|
||||||
|
|
||||||
|
|
||||||
* url remoteUrl
|
|
||||||
|
|
||||||
Given path '/internal-assertion-api/requests'
|
|
||||||
When method get
|
|
||||||
Then status 200
|
|
||||||
|
|
||||||
* url baseUrl
|
|
||||||
|
|
||||||
* print response
|
|
||||||
Then match response.req == ['/users/test-user2']
|
|
||||||
|
|
||||||
Scenario: user-inboxにHTTP Signatureがないリクエストがきたら401を返す
|
|
||||||
|
|
||||||
* def inbox =
|
|
||||||
"""
|
|
||||||
{"type": "Follow"}
|
|
||||||
"""
|
|
||||||
|
|
||||||
Given path '/inbox'
|
|
||||||
And request inbox
|
|
||||||
When method post
|
|
||||||
Then status 401
|
|
||||||
|
|
||||||
|
|
||||||
Scenario: inboxにContent-Type application/json以外が来たら415を返す
|
|
||||||
|
|
||||||
* def inbox =
|
|
||||||
"""
|
|
||||||
{"type": "Follow"}
|
|
||||||
"""
|
|
||||||
|
|
||||||
Given path '/inbox'
|
|
||||||
And request inbox
|
|
||||||
And header Signature = 'keyId="'+ remoteUrl +'/users/test-user#pubkey", algorithm="rsa-sha256", headers="(request-target) date host digest", signature="FfpkmBogW70FMo94yovGpl15L/m4bDjVIFb9mSZUstPE3H00nHiqNsjAq671qFMJsGOO1uWfLEExcdvzwTiC3wuHShzingvxQUbTgcgRTRZcHbtrOZxT8hYHGndpCXGv/NOLkfXDtZO9v5u0fnA2yJFokzyPHOPJ1cJliWlXP38Bl/pO4H5rBLQBZKpM2jYIjMyI78G2rDXNHEeGrGiyfB5SKb3H6zFQL+X9QpXUI4n0f07VsnwaDyp63oUopmzNUyBEuSqB+8va/lbfcWwrxpZnKGzQRZ+VBcV7jDoKGNOP9/O1xEI2CwB8sh+h6KVHdX3EQEvO1slaaLzcwRRqrQ=="'
|
|
||||||
And header Accept = 'application/activity+json'
|
|
||||||
And header Content-Type = 'application/json'
|
|
||||||
When method post
|
|
||||||
Then status 202
|
|
||||||
|
|
||||||
Given path '/inbox'
|
|
||||||
And request inbox
|
|
||||||
And header Signature = 'keyId="'+ remoteUrl +'/users/test-user#pubkey", algorithm="rsa-sha256", headers="(request-target) date host digest", signature="FfpkmBogW70FMo94yovGpl15L/m4bDjVIFb9mSZUstPE3H00nHiqNsjAq671qFMJsGOO1uWfLEExcdvzwTiC3wuHShzingvxQUbTgcgRTRZcHbtrOZxT8hYHGndpCXGv/NOLkfXDtZO9v5u0fnA2yJFokzyPHOPJ1cJliWlXP38Bl/pO4H5rBLQBZKpM2jYIjMyI78G2rDXNHEeGrGiyfB5SKb3H6zFQL+X9QpXUI4n0f07VsnwaDyp63oUopmzNUyBEuSqB+8va/lbfcWwrxpZnKGzQRZ+VBcV7jDoKGNOP9/O1xEI2CwB8sh+h6KVHdX3EQEvO1slaaLzcwRRqrQ=="'
|
|
||||||
And header Accept = 'application/activity+json'
|
|
||||||
And header Content-Type = 'application/activity+json'
|
|
||||||
When method post
|
|
||||||
Then status 202
|
|
||||||
|
|
||||||
Given path '/inbox'
|
|
||||||
And request inbox
|
|
||||||
And header Signature = 'keyId="'+ remoteUrl +'/users/test-user#pubkey", algorithm="rsa-sha256", headers="(request-target) date host digest", signature="FfpkmBogW70FMo94yovGpl15L/m4bDjVIFb9mSZUstPE3H00nHiqNsjAq671qFMJsGOO1uWfLEExcdvzwTiC3wuHShzingvxQUbTgcgRTRZcHbtrOZxT8hYHGndpCXGv/NOLkfXDtZO9v5u0fnA2yJFokzyPHOPJ1cJliWlXP38Bl/pO4H5rBLQBZKpM2jYIjMyI78G2rDXNHEeGrGiyfB5SKb3H6zFQL+X9QpXUI4n0f07VsnwaDyp63oUopmzNUyBEuSqB+8va/lbfcWwrxpZnKGzQRZ+VBcV7jDoKGNOP9/O1xEI2CwB8sh+h6KVHdX3EQEvO1slaaLzcwRRqrQ=="'
|
|
||||||
And header Accept = 'application/activity+json'
|
|
||||||
And header Content-Type = 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"'
|
|
||||||
When method post
|
|
||||||
Then status 202
|
|
||||||
|
|
||||||
Given path '/inbox'
|
|
||||||
And header Signature = 'keyId="'+ remoteUrl +'/users/test-user#pubkey", algorithm="rsa-sha256", headers="(request-target) date host digest", signature="FfpkmBogW70FMo94yovGpl15L/m4bDjVIFb9mSZUstPE3H00nHiqNsjAq671qFMJsGOO1uWfLEExcdvzwTiC3wuHShzingvxQUbTgcgRTRZcHbtrOZxT8hYHGndpCXGv/NOLkfXDtZO9v5u0fnA2yJFokzyPHOPJ1cJliWlXP38Bl/pO4H5rBLQBZKpM2jYIjMyI78G2rDXNHEeGrGiyfB5SKb3H6zFQL+X9QpXUI4n0f07VsnwaDyp63oUopmzNUyBEuSqB+8va/lbfcWwrxpZnKGzQRZ+VBcV7jDoKGNOP9/O1xEI2CwB8sh+h6KVHdX3EQEvO1slaaLzcwRRqrQ=="'
|
|
||||||
And header Accept = 'application/activity+json'
|
|
||||||
When method post
|
|
||||||
Then status 415
|
|
||||||
|
|
||||||
* def html =
|
|
||||||
"""
|
|
||||||
<html>
|
|
||||||
|
|
||||||
</html>
|
|
||||||
"""
|
|
||||||
|
|
||||||
Given path '/inbox'
|
|
||||||
And header Signature = 'keyId="'+ remoteUrl +'/users/test-user#pubkey", algorithm="rsa-sha256", headers="(request-target) date host digest", signature="FfpkmBogW70FMo94yovGpl15L/m4bDjVIFb9mSZUstPE3H00nHiqNsjAq671qFMJsGOO1uWfLEExcdvzwTiC3wuHShzingvxQUbTgcgRTRZcHbtrOZxT8hYHGndpCXGv/NOLkfXDtZO9v5u0fnA2yJFokzyPHOPJ1cJliWlXP38Bl/pO4H5rBLQBZKpM2jYIjMyI78G2rDXNHEeGrGiyfB5SKb3H6zFQL+X9QpXUI4n0f07VsnwaDyp63oUopmzNUyBEuSqB+8va/lbfcWwrxpZnKGzQRZ+VBcV7jDoKGNOP9/O1xEI2CwB8sh+h6KVHdX3EQEvO1slaaLzcwRRqrQ=="'
|
|
||||||
And header Accept = 'application/activity+json'
|
|
||||||
And header Content-Type = 'text/html'
|
|
||||||
And request html
|
|
||||||
When method post
|
|
||||||
Then status 415
|
|
|
@ -1,136 +0,0 @@
|
||||||
Feature: InboxCommonMockServer
|
|
||||||
|
|
||||||
Background:
|
|
||||||
* def assertInbox = Java.type(`federation.InboxCommonTest`)
|
|
||||||
* def req = {req: []}
|
|
||||||
|
|
||||||
Scenario: pathMatches('/users/{username}') && methodIs('get')
|
|
||||||
* def remoteUrl = 'http://localhost:' + assertInbox.getRemotePort()
|
|
||||||
* def username = pathParams.username
|
|
||||||
* def userUrl = remoteUrl + '/users/' + username
|
|
||||||
|
|
||||||
|
|
||||||
* def person =
|
|
||||||
"""
|
|
||||||
{
|
|
||||||
"@context": [
|
|
||||||
"https://www.w3.org/ns/activitystreams",
|
|
||||||
"https://w3id.org/security/v1",
|
|
||||||
{
|
|
||||||
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
|
|
||||||
"toot": "http://joinmastodon.org/ns#",
|
|
||||||
"featured": {
|
|
||||||
"@id": "toot:featured",
|
|
||||||
"@type": "@id"
|
|
||||||
},
|
|
||||||
"featuredTags": {
|
|
||||||
"@id": "toot:featuredTags",
|
|
||||||
"@type": "@id"
|
|
||||||
},
|
|
||||||
"alsoKnownAs": {
|
|
||||||
"@id": "as:alsoKnownAs",
|
|
||||||
"@type": "@id"
|
|
||||||
},
|
|
||||||
"movedTo": {
|
|
||||||
"@id": "as:movedTo",
|
|
||||||
"@type": "@id"
|
|
||||||
},
|
|
||||||
"schema": "http://schema.org#",
|
|
||||||
"PropertyValue": "schema:PropertyValue",
|
|
||||||
"value": "schema:value",
|
|
||||||
"discoverable": "toot:discoverable",
|
|
||||||
"Device": "toot:Device",
|
|
||||||
"Ed25519Signature": "toot:Ed25519Signature",
|
|
||||||
"Ed25519Key": "toot:Ed25519Key",
|
|
||||||
"Curve25519Key": "toot:Curve25519Key",
|
|
||||||
"EncryptedMessage": "toot:EncryptedMessage",
|
|
||||||
"publicKeyBase64": "toot:publicKeyBase64",
|
|
||||||
"deviceId": "toot:deviceId",
|
|
||||||
"claim": {
|
|
||||||
"@type": "@id",
|
|
||||||
"@id": "toot:claim"
|
|
||||||
},
|
|
||||||
"fingerprintKey": {
|
|
||||||
"@type": "@id",
|
|
||||||
"@id": "toot:fingerprintKey"
|
|
||||||
},
|
|
||||||
"identityKey": {
|
|
||||||
"@type": "@id",
|
|
||||||
"@id": "toot:identityKey"
|
|
||||||
},
|
|
||||||
"devices": {
|
|
||||||
"@type": "@id",
|
|
||||||
"@id": "toot:devices"
|
|
||||||
},
|
|
||||||
"messageFranking": "toot:messageFranking",
|
|
||||||
"messageType": "toot:messageType",
|
|
||||||
"cipherText": "toot:cipherText",
|
|
||||||
"suspended": "toot:suspended",
|
|
||||||
"focalPoint": {
|
|
||||||
"@container": "@list",
|
|
||||||
"@id": "toot:focalPoint"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"id": #(userUrl),
|
|
||||||
"type": "Person",
|
|
||||||
"following": #(userUrl + '/following'),
|
|
||||||
"followers": #(userUrl + '/followers'),
|
|
||||||
"inbox": #(userUrl + '/inbox'),
|
|
||||||
"outbox": #(userUrl + '/outbox'),
|
|
||||||
"featured": #(userUrl + '/collections/featured'),
|
|
||||||
"featuredTags": #(userUrl + '/collections/tags'),
|
|
||||||
"preferredUsername": #(username),
|
|
||||||
"name": #(username),
|
|
||||||
"summary": "E2E Test User Jaga/Cotlin/Winter Boot/Ktol\nYonTude: https://example.com\nY(Tvvitter): https://example.com\n",
|
|
||||||
"url": #(userUrl + '/@' + username),
|
|
||||||
"manuallyApprovesFollowers": false,
|
|
||||||
"discoverable": true,
|
|
||||||
"published": "2016-03-16T00:00:00Z",
|
|
||||||
"devices": #(userUrl + '/collections/devices'),
|
|
||||||
"alsoKnownAs": [
|
|
||||||
#( 'https://example.com/users/' + username)
|
|
||||||
],
|
|
||||||
"publicKey": {
|
|
||||||
"id": #(userUrl + '#main-key'),
|
|
||||||
"owner": #(userUrl),
|
|
||||||
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvmtKo0xYGXR4M0LQQhK4\nBkKpRvvUxrqGV6Ew4CBHSyzdnbFsiBqHUWz4JvRiQvAPqQ4jQFpxVZCPr9xx6lJp\nx7EAAKIdVTnBBV4CYfu7QPsRqtjbB5408q+mo5oUXNs8xg2tcC42p2SJ5CRJX/fr\nOgCZwo3LC9pOBdCQZ+tiiPmWNBTNby99JZn4D/xNcwuhV04qcPoHYD9OPuxxGyzc\naVJ2mqJmvi/lewQuR8qnUIbz+Gik+xvyG6LmyuDoa1H2LDQfQXYb62G70HsYdu7a\ndObvZovytp+kkjP/cUaIYkhhOAYqAA4zCwVRY4NHK0MAMq9sMoUfNJa8U+zR9NvD\noQIDAQAB\n-----END PUBLIC KEY-----\n"
|
|
||||||
},
|
|
||||||
"tag": [],
|
|
||||||
"attachment": [
|
|
||||||
{
|
|
||||||
"type": "PropertyValue",
|
|
||||||
"name": "Pixib Fan-Bridge",
|
|
||||||
"value": "\u003ca href=\"https://example.com/hideout\" target=\"_blank\" rel=\"nofollow noopener noreferrer me\"\u003e\u003cspan class=\"invisible\"\u003ehttps://www.\u003c/span\u003e\u003cspan class=\"\"\u003eexample.com/hideout\u003c/span\u003e\u003cspan class=\"invisible\"\u003e\u003c/span\u003e\u003c/a\u003e"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "PropertyValue",
|
|
||||||
"name": "GitHub",
|
|
||||||
"value": "\u003ca href=\"https://github.com/usbharu/hideout\" target=\"_blank\" rel=\"nofollow noopener noreferrer me\"\u003e\u003cspan class=\"invisible\"\u003ehttps://\u003c/span\u003e\u003cspan class=\"\"\u003egithub.com/usbharu/hideout\u003c/span\u003e\u003cspan class=\"invisible\"\u003e\u003c/span\u003e\u003c/a\u003e"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"endpoints": {
|
|
||||||
"sharedInbox": #(remoteUrl + '/inbox')
|
|
||||||
},
|
|
||||||
"icon": {
|
|
||||||
"type": "Image",
|
|
||||||
"mediaType": "image/jpeg",
|
|
||||||
"url": "https://upload.wikimedia.org/wikipedia/commons/thumb/3/30/Destroyer_Vozbuzhdenyy.jpg/320px-Destroyer_Vozbuzhdenyy.jpg"
|
|
||||||
},
|
|
||||||
"image": {
|
|
||||||
"type": "Image",
|
|
||||||
"mediaType": "image/jpeg",
|
|
||||||
"url": "https://upload.wikimedia.org/wikipedia/commons/thumb/6/63/Views_of_Mount_Fuji_from_%C5%8Cwakudani_20211202.jpg/320px-Views_of_Mount_Fuji_from_%C5%8Cwakudani_20211202.jpg"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
"""
|
|
||||||
* set req.req[] = '/users/' + username
|
|
||||||
* def response = person
|
|
||||||
|
|
||||||
Scenario: pathMatches('/internal-assertion-api/requests') && methodIs('get')
|
|
||||||
* def response = req
|
|
||||||
|
|
||||||
Scenario: pathMatches('/internal-assertion-api/requests/deleteAll') && methodIs('post')
|
|
||||||
* set req.req = []
|
|
||||||
* def responseStatus = 200
|
|
|
@ -1,30 +0,0 @@
|
||||||
function fn() {
|
|
||||||
var env = karate.env; // get java system property 'karate.env'
|
|
||||||
karate.log('karate.env system property was:', env);
|
|
||||||
if (!env) {
|
|
||||||
env = 'dev'; // a custom 'intelligent' default
|
|
||||||
karate.log('karate.env set to "dev" as default.');
|
|
||||||
}
|
|
||||||
let config;
|
|
||||||
if (env === 'test') {
|
|
||||||
let remotePort = karate.properties['karate.remotePort'] || '8081'
|
|
||||||
config = {
|
|
||||||
baseUrl: 'https://test-hideout.usbharu.dev',
|
|
||||||
remoteUrl: 'http://localhost:' + remotePort
|
|
||||||
}
|
|
||||||
} else if (env === 'dev') {
|
|
||||||
let port = karate.properties['karate.port'] || '8080'
|
|
||||||
let remotePort = karate.properties['karate.remotePort'] || '8081'
|
|
||||||
config = {
|
|
||||||
baseUrl: 'http://localhost:' + port,
|
|
||||||
remoteUrl: 'http://localhost:' + remotePort
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw 'Unknown environment [' + env + '].'
|
|
||||||
}
|
|
||||||
// don't waste time waiting for a connection or if servers don't respond within 0,3 seconds
|
|
||||||
|
|
||||||
karate.configure('connectTimeout', 1000);
|
|
||||||
karate.configure('readTimeout', 1000);
|
|
||||||
return config;
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
<configuration>
|
|
||||||
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
|
|
||||||
<file>./e2eTest.log</file>
|
|
||||||
<encoder>
|
|
||||||
<charset>UTF-8</charset>
|
|
||||||
<pattern>%d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level [%X{x-request-id}] %logger{36} - %msg%n</pattern>
|
|
||||||
</encoder>
|
|
||||||
</appender>
|
|
||||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
|
||||||
<encoder>
|
|
||||||
<pattern>%d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level [%X{x-request-id}] %logger{36} - %msg%n</pattern>
|
|
||||||
</encoder>
|
|
||||||
</appender>
|
|
||||||
<root level="TRACE">
|
|
||||||
<appender-ref ref="STDOUT"/>
|
|
||||||
<appender-ref ref="FILE"/>
|
|
||||||
</root>
|
|
||||||
<logger name="org.springframework.security" level="TRACE"/>
|
|
||||||
<logger name="com.intuit.karate.driver" level="INFO"/>
|
|
||||||
<logger name="org.thymeleaf.TemplateEngine.CONFIG" level="INFO"/>
|
|
||||||
</configuration>
|
|
|
@ -1,95 +0,0 @@
|
||||||
Feature: OAuth2 Login Test
|
|
||||||
|
|
||||||
Background:
|
|
||||||
* url baseUrl
|
|
||||||
* configure driver = { type: 'chrome',start: true, headless: true, showDriverLog: true, addOptions: [ '--headless=new' ] }
|
|
||||||
|
|
||||||
Scenario: スコープwrite readを持ったトークンの作成
|
|
||||||
|
|
||||||
* def apps =
|
|
||||||
"""
|
|
||||||
{
|
|
||||||
"client_name": "oauth2-test-client-1",
|
|
||||||
"redirect_uris": "https://usbharu.dev",
|
|
||||||
"scopes": "write read"
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
|
|
||||||
Given path '/api/v1/apps'
|
|
||||||
And request apps
|
|
||||||
When method post
|
|
||||||
Then status 200
|
|
||||||
|
|
||||||
* def client_id = response.client_id
|
|
||||||
* def client_secret = response.client_secret
|
|
||||||
|
|
||||||
* def authorizeEndpoint = baseUrl + '/oauth/authorize?response_type=code&redirect_uri=https://usbharu.dev&client_id=' + client_id + '&scope=write%20read'
|
|
||||||
|
|
||||||
Given driver authorizeEndpoint
|
|
||||||
And driver.input('#username','test-user')
|
|
||||||
And driver.input('#password','password')
|
|
||||||
|
|
||||||
When driver.submit().click('body > div > form > button')
|
|
||||||
Then driver.waitForUrl(authorizeEndpoint + "&continue")
|
|
||||||
And driver.click('#read')
|
|
||||||
And driver.click('#write')
|
|
||||||
|
|
||||||
When driver.submit().click('#submit-consent')
|
|
||||||
Then driver.waitUntil("location.host == 'usbharu.dev'")
|
|
||||||
|
|
||||||
* def code = script("new URLSearchParams(document.location.search).get('code')")
|
|
||||||
|
|
||||||
Given path '/oauth/token'
|
|
||||||
And form field client_id = client_id
|
|
||||||
And form field client_secret = client_secret
|
|
||||||
And form field redirect_uri = 'https://usbharu.dev'
|
|
||||||
And form field grant_type = 'authorization_code'
|
|
||||||
And form field code = code
|
|
||||||
And form field scope = 'write read'
|
|
||||||
When method post
|
|
||||||
Then status 200
|
|
||||||
|
|
||||||
Scenario: スコープread:statuses write:statusesを持ったトークンの作成
|
|
||||||
|
|
||||||
* def apps =
|
|
||||||
"""
|
|
||||||
{
|
|
||||||
"client_name": "oauth2-test-client-2",
|
|
||||||
"redirect_uris": "https://usbharu.dev",
|
|
||||||
"scopes": "read:statuses write:statuses"
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
|
|
||||||
Given path '/api/v1/apps'
|
|
||||||
And request apps
|
|
||||||
When method post
|
|
||||||
Then status 200
|
|
||||||
|
|
||||||
* def client_id = response.client_id
|
|
||||||
* def client_secret = response.client_secret
|
|
||||||
|
|
||||||
* def authorizeEndpoint = baseUrl + '/oauth/authorize?response_type=code&redirect_uri=https://usbharu.dev&client_id=' + client_id + '&scope=read:statuses+write:statuses'
|
|
||||||
|
|
||||||
Given driver authorizeEndpoint
|
|
||||||
And driver.input('#username','test-user')
|
|
||||||
And driver.input('#password','password')
|
|
||||||
|
|
||||||
When driver.submit().click('body > div > form > button')
|
|
||||||
Then driver.waitForUrl(authorizeEndpoint + "&continue")
|
|
||||||
And driver.click('/html/body/div/div[4]/div/form/div[1]/input')
|
|
||||||
And driver.click('/html/body/div/div[4]/div/form/div[2]/input')
|
|
||||||
|
|
||||||
When driver.submit().click('#submit-consent')
|
|
||||||
Then driver.waitUntil("location.host == 'usbharu.dev'")
|
|
||||||
|
|
||||||
* def code = script("new URLSearchParams(document.location.search).get('code')")
|
|
||||||
|
|
||||||
Given path '/oauth/token'
|
|
||||||
And form field client_id = client_id
|
|
||||||
And form field client_secret = client_secret
|
|
||||||
And form field redirect_uri = 'https://usbharu.dev'
|
|
||||||
And form field grant_type = 'authorization_code'
|
|
||||||
And form field code = code
|
|
||||||
And form field scope = 'write read'
|
|
||||||
When method post
|
|
||||||
Then status 200
|
|
|
@ -1,51 +0,0 @@
|
||||||
insert into actors (id, name, domain, screen_name, description, inbox, outbox, url, public_key, private_key, created_at,
|
|
||||||
key_id, following, followers, instance, locked, following_count, followers_count, posts_count,
|
|
||||||
last_post_at)
|
|
||||||
VALUES (1730415786666758144, 'test-user', 'localhost', 'Im test user.', 'THis account is test user.',
|
|
||||||
'http://localhost/users/test-user/inbox',
|
|
||||||
'http://localhost/users/test-user/outbox', 'http://localhost/users/test-user',
|
|
||||||
'-----BEGIN PUBLIC KEY-----
|
|
||||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAi4mifRg6huAIn6DXk3Vn
|
|
||||||
5tkRC0AO32ZJvczwXr9xDj4HJvrSUHBAxIwwIeuCceAYtiuZk4JmEKydeB6SRkoO
|
|
||||||
Nty93XZXS1SMmiHCvWOY5YlpnfFU1kLqW3fkXcLNls4XmzujLt1i2sT8mYkENAsP
|
|
||||||
h6K4SRtmktOVYZOWcVEcfLGKbJvaDD/+lKikNC1XCouylfGV/bA/FPY5vuI+7cdM
|
|
||||||
Mjana28JdiWlPWSdzcxtCSgN+nGWPjk2WWm8K+wK2zXqMxA0U0p4odyyILBGALxX
|
|
||||||
zMqObIQvpwPh/t+b6ohem4eq70/0/SwDhd+IzHkT3x4UzG1oxSQS/juPkO7uuS8p
|
|
||||||
uwIDAQAB
|
|
||||||
-----END PUBLIC KEY-----
|
|
||||||
',
|
|
||||||
'-----BEGIN PRIVATE KEY-----
|
|
||||||
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCLiaJ9GDqG4Aif
|
|
||||||
oNeTdWfm2RELQA7fZkm9zPBev3EOPgcm+tJQcEDEjDAh64Jx4Bi2K5mTgmYQrJ14
|
|
||||||
HpJGSg423L3ddldLVIyaIcK9Y5jliWmd8VTWQupbd+Rdws2WzhebO6Mu3WLaxPyZ
|
|
||||||
iQQ0Cw+HorhJG2aS05Vhk5ZxURx8sYpsm9oMP/6UqKQ0LVcKi7KV8ZX9sD8U9jm+
|
|
||||||
4j7tx0wyNqdrbwl2JaU9ZJ3NzG0JKA36cZY+OTZZabwr7ArbNeozEDRTSnih3LIg
|
|
||||||
sEYAvFfMyo5shC+nA+H+35vqiF6bh6rvT/T9LAOF34jMeRPfHhTMbWjFJBL+O4+Q
|
|
||||||
7u65Lym7AgMBAAECggEADJLa7v3LbFLsxAGY22NFeRJPTF252VycwXshn9ANbnSd
|
|
||||||
bWBFqlTrKSrevXe82ekRIP09ygKCkvcS+3t5v9a1gDEU9MtQo2ubfdoT87/xS6G9
|
|
||||||
wCs6c1I1Twe3LtG6d9/bVbQiiLsPSNpeTrF/jPcAL780bvYGoK1rNQ85C7383Kl6
|
|
||||||
1nwZCD0itjkmzbO0nGMRCduW46OdQKiOMuEC7z0zwynH3cK3wGvdlKyLG4L3pPZm
|
|
||||||
1/Uz7AZTieqSCjSgcgmaut7dmS49e3j8ujfb3wcKscfHoofyqNWsW1xyU1WytO9a
|
|
||||||
QLh9wlqfvGlfwQWkY6z6uFmc4XfRVZSC8nic4cAW3QKBgQC4PYbR5AuylDcfc6Am
|
|
||||||
jpL5mcF6qEMnEPgnL9z5VvuLs1f/JEyx5VgzQreDOKc1KOxDX7Xhok4gpvIJv1fi
|
|
||||||
zimviszEmIpHdPvgS7mP2hu42bSIjwVaXpny5aEEZbB6HQ9pGDW/MSsgmb6x31Kx
|
|
||||||
o+sslpqf9cpalI35UPtkNaEJNwKBgQDB4tVUQ5gGPKllEfCN64B/B7wodWr5cUNU
|
|
||||||
UpUXdFPCu+HXnRen6GKLo+25wmCUGtcIuvCY1Xm+tL0Z7jrI+oOD4CL9ob7BJrPF
|
|
||||||
XCq0jUhaEzWFGp1SOa6n+35fWPkCfG4EwfsK8+PWoZsZc1eykMxIJmBln3vufuHz
|
|
||||||
qybfhy0VnQKBgD2tAxvyXmQar9VMjLk7k0IRUa6w80H5sUjVAgFKOA0NLZEQ4sfO
|
|
||||||
wdbvJ6W66mamW2k2ehmdjs/pcy8GKfKYF2ZXbbMGaYwAQm1UjDr2xb78yi3Iyv70
|
|
||||||
mk6wxlVFgW1vmwAQhbWKTSitryO2YeVrvUeA5yRTULk/78Mdc/qY5V7DAoGAAu3I
|
|
||||||
RzOWMlHsRSiWN66dDE4zm3DaotYBLF7q/aW2NjTcXoNy/ghWpMFfL/UtvE8DfJBG
|
|
||||||
XiirZCQazy94F90g63cRUD+HQCezg4G2629O7n1ny5DxW3Kfns3/xLT1XgI/Lzc2
|
|
||||||
8Z1pja53R1Ukt//T9isOPbrBBoNIKoQlXC8QkUkCgYEAsib3uOMAIOJab5jc8FSj
|
|
||||||
VG+Cg2H63J5DgUUwx2Y0DPENugdGyYzCDMVPBNaB0Ru1SpqbUjgqh+YHynunSVeu
|
|
||||||
hDXMOteeyeVHUGw8mvcCEt53uRYVNW/rzXTMqfLVxbsJZHCsJBtFpwcgD2w4NjS2
|
|
||||||
Ja15+ZWbOA4vJA9pOh3x4XM=
|
|
||||||
-----END PRIVATE KEY-----
|
|
||||||
', 1701398248417,
|
|
||||||
'http://localhost/users/test-user#pubkey', 'http://localhost/users/test-user/following',
|
|
||||||
'http://localhost/users/test-users/followers', 0, false, 0, 0, 0, null);
|
|
||||||
|
|
||||||
insert into user_details (actor_id, password, auto_accept_followee_follow_request)
|
|
||||||
values ( 1730415786666758144
|
|
||||||
, '$2a$10$/mWC/n7nC7X3l9qCEOKnredxne2zewoqEsJWTOdlKfg2zXKJ0F9Em', true)
|
|
|
@ -1,163 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package activitypub.inbox
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.SpringApplication
|
|
||||||
import dev.usbharu.hideout.util.Base64Util
|
|
||||||
import dev.usbharu.owl.producer.api.OwlProducer
|
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import org.flywaydb.core.Flyway
|
|
||||||
import org.junit.jupiter.api.AfterAll
|
|
||||||
import org.junit.jupiter.api.BeforeEach
|
|
||||||
import org.junit.jupiter.api.Test
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired
|
|
||||||
import org.springframework.beans.factory.annotation.Qualifier
|
|
||||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc
|
|
||||||
import org.springframework.boot.test.context.SpringBootTest
|
|
||||||
import org.springframework.boot.test.context.TestConfiguration
|
|
||||||
import org.springframework.context.annotation.Bean
|
|
||||||
import org.springframework.http.MediaType
|
|
||||||
import org.springframework.security.test.context.support.WithAnonymousUser
|
|
||||||
import org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity
|
|
||||||
import org.springframework.test.web.servlet.MockMvc
|
|
||||||
import org.springframework.test.web.servlet.post
|
|
||||||
import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder
|
|
||||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders
|
|
||||||
import org.springframework.transaction.annotation.Transactional
|
|
||||||
import org.springframework.web.context.WebApplicationContext
|
|
||||||
import util.TestTransaction
|
|
||||||
import util.WithMockHttpSignature
|
|
||||||
import java.security.MessageDigest
|
|
||||||
import java.time.ZonedDateTime
|
|
||||||
import java.time.format.DateTimeFormatter
|
|
||||||
|
|
||||||
@SpringBootTest(classes = [SpringApplication::class])
|
|
||||||
@AutoConfigureMockMvc
|
|
||||||
@Transactional
|
|
||||||
class InboxTest {
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
@Qualifier("http")
|
|
||||||
private lateinit var dateTimeFormatter: DateTimeFormatter
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private lateinit var context: WebApplicationContext
|
|
||||||
|
|
||||||
private lateinit var mockMvc: MockMvc
|
|
||||||
|
|
||||||
|
|
||||||
@BeforeEach
|
|
||||||
fun setUp() {
|
|
||||||
mockMvc = MockMvcBuilders.webAppContextSetup(context)
|
|
||||||
.apply<DefaultMockMvcBuilder>(springSecurity())
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@WithAnonymousUser
|
|
||||||
fun `匿名でinboxにPOSTしたら401`() {
|
|
||||||
mockMvc
|
|
||||||
.post("/inbox") {
|
|
||||||
content = "{}"
|
|
||||||
contentType = MediaType.APPLICATION_JSON
|
|
||||||
header("Host", "example.com")
|
|
||||||
header("Date", ZonedDateTime.now().format(dateTimeFormatter))
|
|
||||||
header(
|
|
||||||
"Digest",
|
|
||||||
"SHA-256=" + Base64Util.encode(MessageDigest.getInstance("SHA-256").digest("{}".toByteArray()))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect { status { isUnauthorized() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@WithMockHttpSignature
|
|
||||||
fun 有効なHttpSignatureでPOSTしたら202() {
|
|
||||||
mockMvc
|
|
||||||
.post("/inbox") {
|
|
||||||
content = "{}"
|
|
||||||
contentType = MediaType.APPLICATION_JSON
|
|
||||||
header("Signature", "a")
|
|
||||||
header("Host", "example.com")
|
|
||||||
header("Date", ZonedDateTime.now().format(dateTimeFormatter))
|
|
||||||
header(
|
|
||||||
"Digest",
|
|
||||||
"SHA-256=" + Base64Util.encode(MessageDigest.getInstance("SHA-256").digest("{}".toByteArray()))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect { status { isAccepted() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@WithAnonymousUser
|
|
||||||
fun `匿名でuser-inboxにPOSTしたら401`() {
|
|
||||||
mockMvc
|
|
||||||
.post("/users/hoge/inbox") {
|
|
||||||
content = "{}"
|
|
||||||
contentType = MediaType.APPLICATION_JSON
|
|
||||||
header("Host", "example.com")
|
|
||||||
header("Date", ZonedDateTime.now().format(dateTimeFormatter))
|
|
||||||
header(
|
|
||||||
"Digest",
|
|
||||||
"SHA-256=" + Base64Util.encode(MessageDigest.getInstance("SHA-256").digest("{}".toByteArray()))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andDo { print() }
|
|
||||||
.andExpect { status { isUnauthorized() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@WithMockHttpSignature
|
|
||||||
fun 有効なHttpSignaturesでPOSTしたら202() {
|
|
||||||
mockMvc
|
|
||||||
.post("/users/hoge/inbox") {
|
|
||||||
content = "{}"
|
|
||||||
contentType = MediaType.APPLICATION_JSON
|
|
||||||
header("Signature", "a")
|
|
||||||
header("Host", "example.com")
|
|
||||||
header("Date", ZonedDateTime.now().format(dateTimeFormatter))
|
|
||||||
header(
|
|
||||||
"Digest",
|
|
||||||
"SHA-256=" + Base64Util.encode(MessageDigest.getInstance("SHA-256").digest("{}".toByteArray()))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andDo { print() }
|
|
||||||
.andExpect { status { isAccepted() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@TestConfiguration
|
|
||||||
class Configuration {
|
|
||||||
@Bean
|
|
||||||
fun testTransaction() = TestTransaction
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
@JvmStatic
|
|
||||||
@AfterAll
|
|
||||||
fun dropDatabase(@Autowired flyway: Flyway, @Autowired owlProducer: OwlProducer) {
|
|
||||||
flyway.clean()
|
|
||||||
flyway.migrate()
|
|
||||||
runBlocking {
|
|
||||||
owlProducer.stop()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,243 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package activitypub.note
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.SpringApplication
|
|
||||||
import dev.usbharu.owl.producer.api.OwlProducer
|
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import org.flywaydb.core.Flyway
|
|
||||||
import org.junit.jupiter.api.AfterAll
|
|
||||||
import org.junit.jupiter.api.BeforeEach
|
|
||||||
import org.junit.jupiter.api.Test
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired
|
|
||||||
import org.springframework.beans.factory.annotation.Qualifier
|
|
||||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc
|
|
||||||
import org.springframework.boot.test.context.SpringBootTest
|
|
||||||
import org.springframework.http.MediaType
|
|
||||||
import org.springframework.security.test.context.support.WithAnonymousUser
|
|
||||||
import org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity
|
|
||||||
import org.springframework.test.context.jdbc.Sql
|
|
||||||
import org.springframework.test.web.servlet.MockMvc
|
|
||||||
import org.springframework.test.web.servlet.get
|
|
||||||
import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder
|
|
||||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders
|
|
||||||
import org.springframework.transaction.annotation.Transactional
|
|
||||||
import org.springframework.web.context.WebApplicationContext
|
|
||||||
import util.WithHttpSignature
|
|
||||||
import util.WithMockHttpSignature
|
|
||||||
import java.time.ZonedDateTime
|
|
||||||
import java.time.format.DateTimeFormatter
|
|
||||||
|
|
||||||
@SpringBootTest(classes = [SpringApplication::class])
|
|
||||||
@AutoConfigureMockMvc
|
|
||||||
@Transactional
|
|
||||||
class NoteTest {
|
|
||||||
private lateinit var mockMvc: MockMvc
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private lateinit var context: WebApplicationContext
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
@Qualifier("http")
|
|
||||||
private lateinit var dateTimeFormatter: DateTimeFormatter
|
|
||||||
|
|
||||||
@BeforeEach
|
|
||||||
fun setUp() {
|
|
||||||
mockMvc = MockMvcBuilders.webAppContextSetup(context).apply<DefaultMockMvcBuilder>(springSecurity()).build()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@WithAnonymousUser
|
|
||||||
@Sql("/sql/note/匿名でpublic投稿を取得できる.sql")
|
|
||||||
fun `匿名でpublic投稿を取得できる`() {
|
|
||||||
mockMvc
|
|
||||||
.get("/users/test-user/posts/1234") {
|
|
||||||
accept(MediaType("application", "activity+json"))
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andDo { print() }
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
.andExpect { content { contentType("application/activity+json") } }
|
|
||||||
.andExpect { jsonPath("\$.type") { value("Note") } }
|
|
||||||
.andExpect { jsonPath("\$.to") { value("https://www.w3.org/ns/activitystreams#Public") } }
|
|
||||||
.andExpect { jsonPath("\$.cc") { value("https://www.w3.org/ns/activitystreams#Public") } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@Sql("/sql/note/匿名でunlisted投稿を取得できる.sql")
|
|
||||||
@WithAnonymousUser
|
|
||||||
fun 匿名でunlisted投稿を取得できる() {
|
|
||||||
mockMvc
|
|
||||||
.get("/users/test-user2/posts/1235") {
|
|
||||||
accept(MediaType("application", "activity+json"))
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andDo { print() }
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
.andExpect { content { contentType("application/activity+json") } }
|
|
||||||
.andExpect { jsonPath("\$.type") { value("Note") } }
|
|
||||||
.andExpect { jsonPath("\$.to") { value("https://example.com/users/test-user2/followers") } }
|
|
||||||
.andExpect { jsonPath("\$.cc") { value("https://www.w3.org/ns/activitystreams#Public") } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@Transactional
|
|
||||||
@WithAnonymousUser
|
|
||||||
@Sql("/sql/note/匿名でfollowers投稿を取得しようとすると404.sql")
|
|
||||||
fun 匿名でfollowers投稿を取得しようとすると404() {
|
|
||||||
mockMvc
|
|
||||||
.get("/users/test-user2/posts/1236") {
|
|
||||||
accept(MediaType("application", "activity+json"))
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect { status { isNotFound() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@WithAnonymousUser
|
|
||||||
fun 匿名でdirect投稿を取得しようとすると404() {
|
|
||||||
mockMvc
|
|
||||||
.get("/users/test-user2/posts/1236") {
|
|
||||||
accept(MediaType("application", "activity+json"))
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect { status { isNotFound() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@Sql("/sql/note/httpSignature認証でフォロワーがpublic投稿を取得できる.sql")
|
|
||||||
@WithHttpSignature(keyId = "https://follower.example.com/users/test-user5#pubkey")
|
|
||||||
fun HttpSignature認証でフォロワーがpublic投稿を取得できる() {
|
|
||||||
mockMvc
|
|
||||||
.get("/users/test-user4/posts/1237") {
|
|
||||||
accept(MediaType("application", "activity+json"))
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andDo { print() }
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
.andExpect { content { contentType("application/activity+json") } }
|
|
||||||
.andExpect { jsonPath("\$.type") { value("Note") } }
|
|
||||||
.andExpect { jsonPath("\$.to") { value("https://www.w3.org/ns/activitystreams#Public") } }
|
|
||||||
.andExpect { jsonPath("\$.cc") { value("https://www.w3.org/ns/activitystreams#Public") } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@Sql("/sql/note/httpSignature認証でフォロワーがunlisted投稿を取得できる.sql")
|
|
||||||
@WithHttpSignature(keyId = "https://follower.example.com/users/test-user7#pubkey")
|
|
||||||
fun httpSignature認証でフォロワーがunlisted投稿を取得できる() {
|
|
||||||
mockMvc
|
|
||||||
.get("/users/test-user6/posts/1238") {
|
|
||||||
accept(MediaType("application", "activity+json"))
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andDo { print() }
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
.andExpect { content { contentType("application/activity+json") } }
|
|
||||||
.andExpect { jsonPath("\$.type") { value("Note") } }
|
|
||||||
.andExpect { jsonPath("\$.to") { value("https://example.com/users/test-user6/followers") } }
|
|
||||||
.andExpect { jsonPath("\$.cc") { value("https://www.w3.org/ns/activitystreams#Public") } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@Sql("/sql/note/httpSignature認証でフォロワーがfollowers投稿を取得できる.sql")
|
|
||||||
@WithHttpSignature(keyId = "https://follower.example.com/users/test-user9#pubkey")
|
|
||||||
fun httpSignature認証でフォロワーがfollowers投稿を取得できる() {
|
|
||||||
mockMvc
|
|
||||||
.get("/users/test-user8/posts/1239") {
|
|
||||||
accept(MediaType("application", "activity+json"))
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andDo { print() }
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
.andExpect { content { contentType("application/activity+json") } }
|
|
||||||
.andExpect { jsonPath("\$.type") { value("Note") } }
|
|
||||||
.andExpect { jsonPath("\$.to") { value("https://example.com/users/test-user8/followers") } }
|
|
||||||
.andExpect { jsonPath("\$.cc") { value("https://example.com/users/test-user8/followers") } }
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@Sql("/sql/note/リプライになっている投稿はinReplyToが存在する.sql")
|
|
||||||
@WithMockHttpSignature
|
|
||||||
fun リプライになっている投稿はinReplyToが存在する() {
|
|
||||||
mockMvc
|
|
||||||
.get("/users/test-user10/posts/1241") {
|
|
||||||
accept(MediaType("application", "activity+json"))
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andDo { print() }
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
.andExpect { content { contentType("application/activity+json") } }
|
|
||||||
.andExpect { jsonPath("\$.type") { value("Note") } }
|
|
||||||
.andExpect { jsonPath("\$.inReplyTo") { value("https://example.com/users/test-user10/posts/1240") } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@Sql("/sql/note/メディア付き投稿はattachmentにDocumentとして画像が存在する.sql")
|
|
||||||
@WithMockHttpSignature
|
|
||||||
fun メディア付き投稿はattachmentにDocumentとして画像が存在する() {
|
|
||||||
mockMvc
|
|
||||||
.get("/users/test-user10/posts/1242") {
|
|
||||||
accept(MediaType("application", "activity+json"))
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andDo { print() }
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
.andExpect { content { contentType("application/activity+json") } }
|
|
||||||
.andExpect { jsonPath("\$.type") { value("Note") } }
|
|
||||||
.andExpect { jsonPath("\$.attachment") { isArray() } }
|
|
||||||
.andExpect { jsonPath("\$.attachment[0].type") { value("Document") } }
|
|
||||||
.andExpect { jsonPath("\$.attachment[0].url") { value("https://example.com/media/test-media.png") } }
|
|
||||||
.andExpect { jsonPath("\$.attachment[1].type") { value("Document") } }
|
|
||||||
.andExpect { jsonPath("\$.attachment[1].url") { value("https://example.com/media/test-media2.png") } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun signatureヘッダーがあるのにhostヘッダーがないと401() {
|
|
||||||
mockMvc
|
|
||||||
.get("/users/test-user10/posts/9999") {
|
|
||||||
accept(MediaType("application", "activity+json"))
|
|
||||||
header("Signature", "a")
|
|
||||||
header("Date", ZonedDateTime.now().format(dateTimeFormatter))
|
|
||||||
|
|
||||||
}
|
|
||||||
.andExpect { status { isUnauthorized() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun signatureヘッダーがあるのにdateヘッダーがないと401() {
|
|
||||||
mockMvc
|
|
||||||
.get("/users/test-user10/posts/9999") {
|
|
||||||
accept(MediaType("application", "activity+json"))
|
|
||||||
header("Signature", "a")
|
|
||||||
header("Host", "example.com")
|
|
||||||
}
|
|
||||||
.andExpect { status { isUnauthorized() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
@JvmStatic
|
|
||||||
@AfterAll
|
|
||||||
fun dropDatabase(@Autowired flyway: Flyway, @Autowired owlProducer: OwlProducer) {
|
|
||||||
flyway.clean()
|
|
||||||
flyway.migrate()
|
|
||||||
runBlocking {
|
|
||||||
owlProducer.stop()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,116 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package activitypub.webfinger
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.SpringApplication
|
|
||||||
import dev.usbharu.hideout.application.external.Transaction
|
|
||||||
import dev.usbharu.owl.producer.api.OwlProducer
|
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import org.flywaydb.core.Flyway
|
|
||||||
import org.junit.jupiter.api.AfterAll
|
|
||||||
import org.junit.jupiter.api.Test
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired
|
|
||||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc
|
|
||||||
import org.springframework.boot.test.context.SpringBootTest
|
|
||||||
import org.springframework.boot.test.context.TestConfiguration
|
|
||||||
import org.springframework.context.annotation.Bean
|
|
||||||
import org.springframework.test.context.jdbc.Sql
|
|
||||||
import org.springframework.test.web.servlet.MockMvc
|
|
||||||
import org.springframework.test.web.servlet.get
|
|
||||||
import org.springframework.transaction.annotation.Transactional
|
|
||||||
import util.TestTransaction
|
|
||||||
import java.net.URL
|
|
||||||
|
|
||||||
@SpringBootTest(classes = [SpringApplication::class])
|
|
||||||
@AutoConfigureMockMvc
|
|
||||||
@Transactional
|
|
||||||
class WebFingerTest {
|
|
||||||
@Autowired
|
|
||||||
private lateinit var mockMvc: MockMvc
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@Sql("/sql/test-user.sql")
|
|
||||||
|
|
||||||
fun `webfinger 存在するユーザーを取得`() {
|
|
||||||
mockMvc
|
|
||||||
.get("/.well-known/webfinger?resource=acct:test-user@example.com")
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
.andExpect { header { string("Content-Type", "application/json") } }
|
|
||||||
.andExpect {
|
|
||||||
jsonPath("\$.subject") {
|
|
||||||
value("acct:test-user@example.com")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.andExpect {
|
|
||||||
jsonPath("\$.links[0].rel") {
|
|
||||||
value("self")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.andExpect {
|
|
||||||
jsonPath("\$.links[0].href") { value("https://example.com/users/test-user") }
|
|
||||||
}
|
|
||||||
.andExpect {
|
|
||||||
jsonPath("\$.links[0].type") {
|
|
||||||
value("application/activity+json")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `webfinger 存在しないユーザーに404`() {
|
|
||||||
mockMvc
|
|
||||||
.get("/.well-known/webfinger?resource=acct:invalid-user-notfound-afdjashfal@example.com")
|
|
||||||
.andExpect { status { isNotFound() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `webfinger 不正なリクエストは400`() {
|
|
||||||
mockMvc
|
|
||||||
.get("/.well-known/webfinger?res=acct:test")
|
|
||||||
.andExpect { status { isBadRequest() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `webfinger acctのパースが出来なくても400`() {
|
|
||||||
mockMvc
|
|
||||||
.get("/.well-known/webfinger?resource=acct:@a@b@c@d")
|
|
||||||
.andExpect { status { isBadRequest() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@TestConfiguration
|
|
||||||
class Configuration {
|
|
||||||
@Bean
|
|
||||||
fun url(): URL {
|
|
||||||
return URL("https://example.com")
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
fun testTransaction(): Transaction = TestTransaction
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
@JvmStatic
|
|
||||||
@AfterAll
|
|
||||||
fun dropDatabase(@Autowired flyway: Flyway, @Autowired owlProducer: OwlProducer) {
|
|
||||||
flyway.clean()
|
|
||||||
flyway.migrate()
|
|
||||||
runBlocking {
|
|
||||||
owlProducer.stop()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,166 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package mastodon.account
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.type.TypeReference
|
|
||||||
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
|
||||||
import dev.usbharu.hideout.SpringApplication
|
|
||||||
import dev.usbharu.hideout.domain.mastodon.model.generated.Status
|
|
||||||
import dev.usbharu.owl.producer.api.OwlProducer
|
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
|
||||||
import org.flywaydb.core.Flyway
|
|
||||||
import org.junit.jupiter.api.AfterAll
|
|
||||||
import org.junit.jupiter.api.BeforeEach
|
|
||||||
import org.junit.jupiter.api.Test
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired
|
|
||||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc
|
|
||||||
import org.springframework.boot.test.context.SpringBootTest
|
|
||||||
import org.springframework.security.core.authority.SimpleGrantedAuthority
|
|
||||||
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors
|
|
||||||
import org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers
|
|
||||||
import org.springframework.test.context.jdbc.Sql
|
|
||||||
import org.springframework.test.web.servlet.MockMvc
|
|
||||||
import org.springframework.test.web.servlet.get
|
|
||||||
import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder
|
|
||||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders
|
|
||||||
import org.springframework.transaction.annotation.Transactional
|
|
||||||
import org.springframework.web.context.WebApplicationContext
|
|
||||||
|
|
||||||
@SpringBootTest(classes = [SpringApplication::class])
|
|
||||||
@AutoConfigureMockMvc
|
|
||||||
@Transactional
|
|
||||||
@Sql("/sql/test-user.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_CLASS)
|
|
||||||
@Sql("/sql/test-user2.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_CLASS)
|
|
||||||
@Sql("/sql/accounts/test-accounts-statuses.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_CLASS)
|
|
||||||
class AccountApiPaginationTest {
|
|
||||||
@Autowired
|
|
||||||
private lateinit var context: WebApplicationContext
|
|
||||||
|
|
||||||
private lateinit var mockMvc: MockMvc
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `apiV1AccountsIdStatusesGet 投稿を取得できる`() {
|
|
||||||
val content = mockMvc
|
|
||||||
.get("/api/v1/accounts/1/statuses"){
|
|
||||||
with(
|
|
||||||
SecurityMockMvcRequestPostProcessors.jwt()
|
|
||||||
.jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
.andExpect { header { string("Link","<https://example.com/api/v1/accounts/1/statuses?min_id=100>; rel=\"next\", <https://example.com/api/v1/accounts/1/statuses?max_id=81>; rel=\"prev\"") } }
|
|
||||||
.andReturn()
|
|
||||||
.response
|
|
||||||
.contentAsString
|
|
||||||
|
|
||||||
val value = jacksonObjectMapper().readValue(content, object : TypeReference<List<Status>>() {})
|
|
||||||
|
|
||||||
assertThat(value.first().id).isEqualTo("100")
|
|
||||||
assertThat(value.last().id).isEqualTo("81")
|
|
||||||
assertThat(value).size().isEqualTo(20)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `apiV1AccountsIdStatusesGet 結果が0件のときはLinkヘッダーがない`() {
|
|
||||||
val content = mockMvc
|
|
||||||
.get("/api/v1/accounts/1/statuses?min_id=100"){
|
|
||||||
with(
|
|
||||||
SecurityMockMvcRequestPostProcessors.jwt()
|
|
||||||
.jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
.andExpect { header { doesNotExist("Link") } }
|
|
||||||
.andReturn()
|
|
||||||
.response
|
|
||||||
.contentAsString
|
|
||||||
|
|
||||||
val value = jacksonObjectMapper().readValue(content, object : TypeReference<List<Status>>() {})
|
|
||||||
|
|
||||||
|
|
||||||
assertThat(value).isEmpty()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `apiV1AccountsIdStatusesGet maxIdを指定して取得`() {
|
|
||||||
val content = mockMvc
|
|
||||||
.get("/api/v1/accounts/1/statuses?max_id=100"){
|
|
||||||
with(
|
|
||||||
SecurityMockMvcRequestPostProcessors.jwt()
|
|
||||||
.jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
.andExpect { header { string("Link","<https://example.com/api/v1/accounts/1/statuses?min_id=99>; rel=\"next\", <https://example.com/api/v1/accounts/1/statuses?max_id=80>; rel=\"prev\"") } }
|
|
||||||
.andReturn()
|
|
||||||
.response
|
|
||||||
.contentAsString
|
|
||||||
|
|
||||||
val value = jacksonObjectMapper().readValue(content, object : TypeReference<List<Status>>() {})
|
|
||||||
|
|
||||||
assertThat(value.first().id).isEqualTo("99")
|
|
||||||
assertThat(value.last().id).isEqualTo("80")
|
|
||||||
assertThat(value).size().isEqualTo(20)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `apiV1AccountsIdStatusesGet minIdを指定して取得`() {
|
|
||||||
val content = mockMvc
|
|
||||||
.get("/api/v1/accounts/1/statuses?min_id=1"){
|
|
||||||
with(
|
|
||||||
SecurityMockMvcRequestPostProcessors.jwt()
|
|
||||||
.jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
.andExpect { header { string("Link","<https://example.com/api/v1/accounts/1/statuses?min_id=21>; rel=\"next\", <https://example.com/api/v1/accounts/1/statuses?max_id=2>; rel=\"prev\"") } }
|
|
||||||
.andReturn()
|
|
||||||
.response
|
|
||||||
.contentAsString
|
|
||||||
|
|
||||||
val value = jacksonObjectMapper().readValue(content, object : TypeReference<List<Status>>() {})
|
|
||||||
|
|
||||||
assertThat(value.first().id).isEqualTo("21")
|
|
||||||
assertThat(value.last().id).isEqualTo("2")
|
|
||||||
assertThat(value).size().isEqualTo(20)
|
|
||||||
}
|
|
||||||
|
|
||||||
@BeforeEach
|
|
||||||
fun setUp() {
|
|
||||||
mockMvc = MockMvcBuilders.webAppContextSetup(context)
|
|
||||||
.apply<DefaultMockMvcBuilder>(SecurityMockMvcConfigurers.springSecurity())
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
@JvmStatic
|
|
||||||
@AfterAll
|
|
||||||
fun dropDatabase(@Autowired flyway: Flyway, @Autowired owlProducer: OwlProducer) {
|
|
||||||
flyway.clean()
|
|
||||||
flyway.migrate()
|
|
||||||
runBlocking {
|
|
||||||
owlProducer.stop()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,479 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package mastodon.account
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.SpringApplication
|
|
||||||
import dev.usbharu.hideout.core.domain.model.actor.ActorRepository
|
|
||||||
import dev.usbharu.hideout.core.infrastructure.exposedquery.FollowerQueryServiceImpl
|
|
||||||
import dev.usbharu.owl.producer.api.OwlProducer
|
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import kotlinx.coroutines.test.runTest
|
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
|
||||||
import org.flywaydb.core.Flyway
|
|
||||||
import org.junit.jupiter.api.AfterAll
|
|
||||||
import org.junit.jupiter.api.BeforeEach
|
|
||||||
import org.junit.jupiter.api.Disabled
|
|
||||||
import org.junit.jupiter.api.Test
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired
|
|
||||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc
|
|
||||||
import org.springframework.boot.test.context.SpringBootTest
|
|
||||||
import org.springframework.http.MediaType
|
|
||||||
import org.springframework.security.core.authority.SimpleGrantedAuthority
|
|
||||||
import org.springframework.security.test.context.support.WithAnonymousUser
|
|
||||||
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf
|
|
||||||
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.jwt
|
|
||||||
import org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity
|
|
||||||
import org.springframework.test.context.jdbc.Sql
|
|
||||||
import org.springframework.test.web.servlet.MockMvc
|
|
||||||
import org.springframework.test.web.servlet.get
|
|
||||||
import org.springframework.test.web.servlet.post
|
|
||||||
import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder
|
|
||||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders
|
|
||||||
import org.springframework.transaction.annotation.Transactional
|
|
||||||
import org.springframework.web.context.WebApplicationContext
|
|
||||||
|
|
||||||
@SpringBootTest(classes = [SpringApplication::class])
|
|
||||||
@AutoConfigureMockMvc
|
|
||||||
@Transactional
|
|
||||||
@Sql("/sql/test-user.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_CLASS)
|
|
||||||
@Sql("/sql/test-user2.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_CLASS)
|
|
||||||
class AccountApiTest {
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private lateinit var followerQueryServiceImpl: FollowerQueryServiceImpl
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private lateinit var actorRepository: ActorRepository
|
|
||||||
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private lateinit var context: WebApplicationContext
|
|
||||||
|
|
||||||
private lateinit var mockMvc: MockMvc
|
|
||||||
|
|
||||||
@BeforeEach
|
|
||||||
fun setUp() {
|
|
||||||
mockMvc = MockMvcBuilders.webAppContextSetup(context)
|
|
||||||
.apply<DefaultMockMvcBuilder>(springSecurity())
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `apiV1AccountsVerifyCredentialsGetにreadでアクセスできる`() {
|
|
||||||
mockMvc
|
|
||||||
.get("/api/v1/accounts/verify_credentials") {
|
|
||||||
with(jwt().jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read")))
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andDo { print() }
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `apiV1AccountsVerifyCredentialsGetにread_accountsでアクセスできる`() {
|
|
||||||
mockMvc
|
|
||||||
.get("/api/v1/accounts/verify_credentials") {
|
|
||||||
with(jwt().jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read:accounts")))
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andDo { print() }
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@WithAnonymousUser
|
|
||||||
fun apiV1AccountsVerifyCredentialsGetに匿名でアクセスすると401() {
|
|
||||||
mockMvc
|
|
||||||
.get("/api/v1/accounts/verify_credentials")
|
|
||||||
.andExpect { status { isUnauthorized() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@WithAnonymousUser
|
|
||||||
fun apiV1AccountsPostに匿名でPOSTしたらアカウントを作成できる() = runTest {
|
|
||||||
mockMvc
|
|
||||||
.post("/api/v1/accounts") {
|
|
||||||
contentType = MediaType.APPLICATION_FORM_URLENCODED
|
|
||||||
param("username", "api-test-user-1")
|
|
||||||
param("password", "very-secure-password")
|
|
||||||
param("email", "test@example.com")
|
|
||||||
param("agreement", "true")
|
|
||||||
param("locale", "")
|
|
||||||
with(jwt())
|
|
||||||
with(csrf())
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect { status { isFound() } }
|
|
||||||
|
|
||||||
actorRepository.findByNameAndDomain("api-test-user-1", "example.com")
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@WithAnonymousUser
|
|
||||||
fun apiV1AccountsPostで必須パラメーター以外を省略しても作成できる() = runTest {
|
|
||||||
mockMvc
|
|
||||||
.post("/api/v1/accounts") {
|
|
||||||
contentType = MediaType.APPLICATION_FORM_URLENCODED
|
|
||||||
param("username", "api-test-user-2")
|
|
||||||
param("password", "very-secure-password")
|
|
||||||
with(jwt())
|
|
||||||
with(csrf())
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect { status { isFound() } }
|
|
||||||
|
|
||||||
actorRepository.findByNameAndDomain("api-test-user-2", "example.com")
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@WithAnonymousUser
|
|
||||||
fun apiV1AccountsPostでusernameパラメーターを省略したら400() = runTest {
|
|
||||||
mockMvc
|
|
||||||
.post("/api/v1/accounts") {
|
|
||||||
contentType = MediaType.APPLICATION_FORM_URLENCODED
|
|
||||||
param("password", "api-test-user-3")
|
|
||||||
with(csrf())
|
|
||||||
with(jwt())
|
|
||||||
}
|
|
||||||
.andDo { print() }
|
|
||||||
.andExpect { status { isUnprocessableEntity() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@WithAnonymousUser
|
|
||||||
fun apiV1AccountsPostでpasswordパラメーターを省略したら400() = runTest {
|
|
||||||
mockMvc
|
|
||||||
.post("/api/v1/accounts") {
|
|
||||||
contentType = MediaType.APPLICATION_FORM_URLENCODED
|
|
||||||
param("username", "api-test-user-4")
|
|
||||||
with(csrf())
|
|
||||||
with(jwt())
|
|
||||||
}
|
|
||||||
.andExpect { status { isUnprocessableEntity() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@Disabled("JSONでも作れるようにするため")
|
|
||||||
@WithAnonymousUser
|
|
||||||
fun apiV1AccountsPostでJSONで作ろうとしても400() {
|
|
||||||
mockMvc
|
|
||||||
.post("/api/v1/accounts") {
|
|
||||||
contentType = MediaType.APPLICATION_JSON
|
|
||||||
content = """{"username":"api-test-user-5","password":"very-very-secure-password"}"""
|
|
||||||
with(csrf())
|
|
||||||
}
|
|
||||||
.andExpect { status { isUnsupportedMediaType() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@WithAnonymousUser
|
|
||||||
fun apiV1AccountsPostにCSRFトークンは必要() {
|
|
||||||
mockMvc
|
|
||||||
.post("/api/v1/accounts") {
|
|
||||||
contentType = MediaType.APPLICATION_FORM_URLENCODED
|
|
||||||
param("username", "api-test-user-2")
|
|
||||||
param("password", "very-secure-password")
|
|
||||||
}
|
|
||||||
.andExpect { status { isForbidden() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@WithAnonymousUser
|
|
||||||
fun `apiV1AccountsIdGet 匿名でアカウント情報を取得できる`() {
|
|
||||||
mockMvc
|
|
||||||
.get("/api/v1/accounts/1")
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `apiV1AccountsIdFollowPost write_follows権限でPOSTでフォローできる`() {
|
|
||||||
mockMvc
|
|
||||||
.post("/api/v1/accounts/2/follow") {
|
|
||||||
contentType = MediaType.APPLICATION_JSON
|
|
||||||
with(jwt().jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write:follows")))
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `apiV1AccountsIdFollowPost write権限でPOSTでフォローできる`() {
|
|
||||||
mockMvc
|
|
||||||
.post("/api/v1/accounts/2/follow") {
|
|
||||||
contentType = MediaType.APPLICATION_JSON
|
|
||||||
with(jwt().jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write")))
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `apiV1AccountsIdFollowPost read権限でだと403`() {
|
|
||||||
mockMvc
|
|
||||||
.post("/api/v1/accounts/2/follow") {
|
|
||||||
contentType = MediaType.APPLICATION_JSON
|
|
||||||
with(jwt().jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read")))
|
|
||||||
}
|
|
||||||
.andExpect { status { isForbidden() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@WithAnonymousUser
|
|
||||||
fun `apiV1AAccountsIdFollowPost 匿名だと401`() {
|
|
||||||
mockMvc
|
|
||||||
.post("/api/v1/accounts/2/follow") {
|
|
||||||
contentType = MediaType.APPLICATION_JSON
|
|
||||||
with(csrf())
|
|
||||||
}
|
|
||||||
.andExpect { status { isUnauthorized() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@WithAnonymousUser
|
|
||||||
fun `apiV1AAccountsIdFollowPost 匿名の場合通常csrfトークンは持ってないので403`() {
|
|
||||||
mockMvc
|
|
||||||
.post("/api/v1/accounts/2/follow") {
|
|
||||||
contentType = MediaType.APPLICATION_JSON
|
|
||||||
}
|
|
||||||
.andExpect { status { isForbidden() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `apiV1AccountsRelationshipsGet 匿名だと401`() {
|
|
||||||
mockMvc
|
|
||||||
.get("/api/v1/accounts/relationships")
|
|
||||||
.andExpect { status { isUnauthorized() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `apiV1AccountsRelationshipsGet read_follows権限を持っていたら取得できる`() {
|
|
||||||
mockMvc
|
|
||||||
.get("/api/v1/accounts/relationships") {
|
|
||||||
with(jwt().jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read:follows")))
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `apiV1AccountsRelationshipsGet read権限を持っていたら取得できる`() {
|
|
||||||
mockMvc
|
|
||||||
.get("/api/v1/accounts/relationships") {
|
|
||||||
with(jwt().jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read")))
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `apiV1AccountsRelationshipsGet write権限だと403`() {
|
|
||||||
mockMvc
|
|
||||||
.get("/api/v1/accounts/relationships") {
|
|
||||||
with(jwt().jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write")))
|
|
||||||
}
|
|
||||||
.andExpect { status { isForbidden() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@Sql("/sql/accounts/apiV1AccountsIdFollowPost フォローできる.sql")
|
|
||||||
fun `apiV1AccountsIdFollowPost フォローできる`() = runTest {
|
|
||||||
mockMvc
|
|
||||||
.post("/api/v1/accounts/3733363/follow") {
|
|
||||||
contentType = MediaType.APPLICATION_JSON
|
|
||||||
with(jwt().jwt { it.claim("uid", "37335363") }.authorities(SimpleGrantedAuthority("SCOPE_write")))
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
|
|
||||||
val alreadyFollow = followerQueryServiceImpl.alreadyFollow(3733363, 37335363)
|
|
||||||
|
|
||||||
assertThat(alreadyFollow).isTrue()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `apiV1AccountsIdMutePost write権限でミュートできる`() {
|
|
||||||
mockMvc
|
|
||||||
.post("/api/v1/accounts/2/mute") {
|
|
||||||
contentType = MediaType.APPLICATION_JSON
|
|
||||||
with(jwt().jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write")))
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `apiV1AccountsIdMutePost write_mutes権限でミュートできる`() {
|
|
||||||
mockMvc
|
|
||||||
.post("/api/v1/accounts/2/mute") {
|
|
||||||
contentType = MediaType.APPLICATION_JSON
|
|
||||||
with(jwt().jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write:mutes")))
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `apiV1AccountsIdMutePost read権限だと403`() = runTest {
|
|
||||||
mockMvc
|
|
||||||
.post("/api/v1/accounts/2/mute") {
|
|
||||||
contentType = MediaType.APPLICATION_JSON
|
|
||||||
with(jwt().jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read")))
|
|
||||||
}
|
|
||||||
.andExpect { status { isForbidden() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@WithAnonymousUser
|
|
||||||
fun `apiV1AccountsIdMutePost 匿名だと401`() = runTest {
|
|
||||||
mockMvc
|
|
||||||
.post("/api/v1/accounts/2/mute") {
|
|
||||||
contentType = MediaType.APPLICATION_JSON
|
|
||||||
with(csrf())
|
|
||||||
}
|
|
||||||
.andExpect { status { isUnauthorized() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@WithAnonymousUser
|
|
||||||
fun `apiV1AccountsIdMutePost csrfトークンがないと403`() = runTest {
|
|
||||||
mockMvc
|
|
||||||
.post("/api/v1/accounts/2/mute") {
|
|
||||||
contentType = MediaType.APPLICATION_JSON
|
|
||||||
}
|
|
||||||
.andExpect { status { isForbidden() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `apiV1AccountsIdUnmutePost write権限でアンミュートできる`() {
|
|
||||||
mockMvc
|
|
||||||
.post("/api/v1/accounts/2/unmute") {
|
|
||||||
contentType = MediaType.APPLICATION_JSON
|
|
||||||
with(jwt().jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write")))
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `apiV1AccountsIdUnmutePost write_mutes権限でアンミュートできる`() {
|
|
||||||
mockMvc
|
|
||||||
.post("/api/v1/accounts/2/unmute") {
|
|
||||||
contentType = MediaType.APPLICATION_JSON
|
|
||||||
with(jwt().jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write:mutes")))
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `apiV1AccountsIdUnmutePost read権限だと403`() = runTest {
|
|
||||||
mockMvc
|
|
||||||
.post("/api/v1/accounts/2/unmute") {
|
|
||||||
contentType = MediaType.APPLICATION_JSON
|
|
||||||
with(jwt().jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read")))
|
|
||||||
}
|
|
||||||
.andExpect { status { isForbidden() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@WithAnonymousUser
|
|
||||||
fun `apiV1AccountsIdUnmutePost 匿名だと401`() = runTest {
|
|
||||||
mockMvc
|
|
||||||
.post("/api/v1/accounts/2/unmute") {
|
|
||||||
contentType = MediaType.APPLICATION_JSON
|
|
||||||
with(csrf())
|
|
||||||
}
|
|
||||||
.andExpect { status { isUnauthorized() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@WithAnonymousUser
|
|
||||||
fun `apiV1AccountsIdUnmutePost csrfトークンがないと403`() = runTest {
|
|
||||||
mockMvc
|
|
||||||
.post("/api/v1/accounts/2/unmute") {
|
|
||||||
contentType = MediaType.APPLICATION_JSON
|
|
||||||
}
|
|
||||||
.andExpect { status { isForbidden() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `apiV1MutesGet read権限でミュートしているアカウント一覧を取得できる`() {
|
|
||||||
mockMvc
|
|
||||||
.get("/api/v1/mutes") {
|
|
||||||
with(jwt().jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read")))
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `apiV1MutesGet read_mutes権限でミュートしているアカウント一覧を取得できる`() {
|
|
||||||
mockMvc
|
|
||||||
.get("/api/v1/mutes") {
|
|
||||||
with(jwt().jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read:mutes")))
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `apiV1MutesGet write権限だと403`() {
|
|
||||||
mockMvc
|
|
||||||
.get("/api/v1/mutes") {
|
|
||||||
with(jwt().jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write")))
|
|
||||||
}
|
|
||||||
.andExpect { status { isForbidden() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@WithAnonymousUser
|
|
||||||
fun `apiV1MutesGet 匿名だと401`() {
|
|
||||||
mockMvc
|
|
||||||
.get("/api/v1/mutes")
|
|
||||||
.andExpect { status { isUnauthorized() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `apiV1AccountsIdStatusesGet read権限で取得できる`() {
|
|
||||||
mockMvc
|
|
||||||
.get("/api/v1/accounts/1/statuses")
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@WithAnonymousUser
|
|
||||||
fun `apiV1AccountsIdStatusesGet 匿名でもpublic投稿を取得できる`() {
|
|
||||||
mockMvc
|
|
||||||
.get("/api/v1/accounts/1/statuses")
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
@JvmStatic
|
|
||||||
@AfterAll
|
|
||||||
fun dropDatabase(@Autowired flyway: Flyway, @Autowired owlProducer: OwlProducer) {
|
|
||||||
flyway.clean()
|
|
||||||
flyway.migrate()
|
|
||||||
runBlocking {
|
|
||||||
owlProducer.stop()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,120 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package mastodon.apps
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.SpringApplication
|
|
||||||
import dev.usbharu.hideout.core.infrastructure.springframework.oauth2.RegisteredClient
|
|
||||||
import dev.usbharu.owl.producer.api.OwlProducer
|
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
|
||||||
import org.flywaydb.core.Flyway
|
|
||||||
import org.jetbrains.exposed.sql.selectAll
|
|
||||||
import org.junit.jupiter.api.AfterAll
|
|
||||||
import org.junit.jupiter.api.BeforeEach
|
|
||||||
import org.junit.jupiter.api.Test
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired
|
|
||||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc
|
|
||||||
import org.springframework.boot.test.context.SpringBootTest
|
|
||||||
import org.springframework.http.MediaType
|
|
||||||
import org.springframework.security.test.context.support.WithAnonymousUser
|
|
||||||
import org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers
|
|
||||||
import org.springframework.test.web.servlet.MockMvc
|
|
||||||
import org.springframework.test.web.servlet.post
|
|
||||||
import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder
|
|
||||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders
|
|
||||||
import org.springframework.transaction.annotation.Transactional
|
|
||||||
import org.springframework.web.context.WebApplicationContext
|
|
||||||
|
|
||||||
@SpringBootTest(classes = [SpringApplication::class])
|
|
||||||
@AutoConfigureMockMvc
|
|
||||||
@Transactional
|
|
||||||
class AppTest {
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private lateinit var context: WebApplicationContext
|
|
||||||
|
|
||||||
private lateinit var mockMvc: MockMvc
|
|
||||||
|
|
||||||
|
|
||||||
@BeforeEach
|
|
||||||
fun setUp() {
|
|
||||||
mockMvc = MockMvcBuilders.webAppContextSetup(context)
|
|
||||||
.apply<DefaultMockMvcBuilder>(SecurityMockMvcConfigurers.springSecurity())
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@WithAnonymousUser
|
|
||||||
fun apiV1AppsPostにformで匿名でappを作成できる() {
|
|
||||||
mockMvc
|
|
||||||
.post("/api/v1/apps") {
|
|
||||||
contentType = MediaType.APPLICATION_FORM_URLENCODED
|
|
||||||
param("client_name", "test-client")
|
|
||||||
param("redirect_uris", "https://example.com")
|
|
||||||
param("scopes", "write read")
|
|
||||||
param("website", "https://example.com")
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
|
|
||||||
|
|
||||||
val app = RegisteredClient
|
|
||||||
.selectAll().where { RegisteredClient.clientName eq "test-client" }
|
|
||||||
.single()
|
|
||||||
|
|
||||||
assertThat(app[RegisteredClient.clientName]).isEqualTo("test-client")
|
|
||||||
assertThat(app[RegisteredClient.redirectUris]).isEqualTo("https://example.com")
|
|
||||||
assertThat(app[RegisteredClient.scopes]).isEqualTo("read,write")
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@WithAnonymousUser
|
|
||||||
fun apiV1AppsPostにjsonで匿名でappを作成できる() {
|
|
||||||
mockMvc
|
|
||||||
.post("/api/v1/apps") {
|
|
||||||
contentType = MediaType.APPLICATION_JSON
|
|
||||||
content = """{
|
|
||||||
"client_name": "test-client-2",
|
|
||||||
"redirect_uris": "https://example.com",
|
|
||||||
"scopes": "write read",
|
|
||||||
"website": "https;//example.com"
|
|
||||||
}"""
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
|
|
||||||
val app = RegisteredClient
|
|
||||||
.selectAll().where { RegisteredClient.clientName eq "test-client-2" }
|
|
||||||
.single()
|
|
||||||
|
|
||||||
assertThat(app[RegisteredClient.clientName]).isEqualTo("test-client-2")
|
|
||||||
assertThat(app[RegisteredClient.redirectUris]).isEqualTo("https://example.com")
|
|
||||||
assertThat(app[RegisteredClient.scopes]).isEqualTo("read,write")
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
@JvmStatic
|
|
||||||
@AfterAll
|
|
||||||
fun dropDatabase(@Autowired flyway: Flyway, @Autowired owlProducer: OwlProducer) {
|
|
||||||
flyway.clean()
|
|
||||||
flyway.migrate()
|
|
||||||
runBlocking {
|
|
||||||
owlProducer.stop()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,715 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package mastodon.filter
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.SpringApplication
|
|
||||||
import dev.usbharu.hideout.application.config.ActivityPubConfig
|
|
||||||
import dev.usbharu.hideout.domain.mastodon.model.generated.FilterKeywordsPostRequest
|
|
||||||
import dev.usbharu.hideout.domain.mastodon.model.generated.FilterPostRequest
|
|
||||||
import dev.usbharu.hideout.domain.mastodon.model.generated.FilterPostRequestKeyword
|
|
||||||
import dev.usbharu.hideout.domain.mastodon.model.generated.V1FilterPostRequest
|
|
||||||
import dev.usbharu.owl.producer.api.OwlProducer
|
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import kotlinx.coroutines.test.runTest
|
|
||||||
import org.flywaydb.core.Flyway
|
|
||||||
import org.junit.jupiter.api.AfterAll
|
|
||||||
import org.junit.jupiter.api.BeforeEach
|
|
||||||
import org.junit.jupiter.api.Test
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired
|
|
||||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc
|
|
||||||
import org.springframework.boot.test.context.SpringBootTest
|
|
||||||
import org.springframework.http.MediaType
|
|
||||||
import org.springframework.security.core.authority.SimpleGrantedAuthority
|
|
||||||
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.jwt
|
|
||||||
import org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers
|
|
||||||
import org.springframework.test.context.jdbc.Sql
|
|
||||||
import org.springframework.test.web.servlet.MockMvc
|
|
||||||
import org.springframework.test.web.servlet.delete
|
|
||||||
import org.springframework.test.web.servlet.get
|
|
||||||
import org.springframework.test.web.servlet.post
|
|
||||||
import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder
|
|
||||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders
|
|
||||||
import org.springframework.transaction.annotation.Transactional
|
|
||||||
import org.springframework.web.context.WebApplicationContext
|
|
||||||
|
|
||||||
@SpringBootTest(classes = [SpringApplication::class])
|
|
||||||
@AutoConfigureMockMvc
|
|
||||||
@Transactional
|
|
||||||
@Sql("/sql/test-user.sql", "/sql/filter/test-filter.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_CLASS)
|
|
||||||
class FilterTest {
|
|
||||||
@Autowired
|
|
||||||
private lateinit var context: WebApplicationContext
|
|
||||||
|
|
||||||
private lateinit var mockMvc: MockMvc
|
|
||||||
|
|
||||||
@BeforeEach
|
|
||||||
fun setUp() {
|
|
||||||
mockMvc = MockMvcBuilders.webAppContextSetup(context)
|
|
||||||
.apply<DefaultMockMvcBuilder>(SecurityMockMvcConfigurers.springSecurity())
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `apiV2FiltersPost write権限で追加できる`() {
|
|
||||||
mockMvc
|
|
||||||
.post("/api/v2/filters") {
|
|
||||||
contentType = MediaType.APPLICATION_JSON
|
|
||||||
content = ActivityPubConfig().objectMapper().writeValueAsString(
|
|
||||||
FilterPostRequest(
|
|
||||||
title = "mute test",
|
|
||||||
context = listOf(FilterPostRequest.Context.home, FilterPostRequest.Context.public),
|
|
||||||
filterAction = FilterPostRequest.FilterAction.warn,
|
|
||||||
expiresIn = null,
|
|
||||||
keywordsAttributes = listOf(
|
|
||||||
FilterPostRequestKeyword(
|
|
||||||
keyword = "hoge",
|
|
||||||
wholeWord = false,
|
|
||||||
regex = true
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
with(
|
|
||||||
jwt()
|
|
||||||
.jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
.andExpect {
|
|
||||||
content {
|
|
||||||
jsonPath("$.keywords[0].keyword") {
|
|
||||||
value("hoge")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `apiV2FiltersPost write_filters権限で追加できる`() {
|
|
||||||
mockMvc
|
|
||||||
.post("/api/v2/filters") {
|
|
||||||
contentType = MediaType.APPLICATION_JSON
|
|
||||||
content = ActivityPubConfig().objectMapper().writeValueAsString(
|
|
||||||
FilterPostRequest(
|
|
||||||
title = "mute test",
|
|
||||||
context = listOf(FilterPostRequest.Context.home, FilterPostRequest.Context.public),
|
|
||||||
filterAction = FilterPostRequest.FilterAction.warn,
|
|
||||||
expiresIn = null,
|
|
||||||
keywordsAttributes = listOf(
|
|
||||||
FilterPostRequestKeyword(
|
|
||||||
keyword = "fuga",
|
|
||||||
wholeWord = true,
|
|
||||||
regex = false
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
with(
|
|
||||||
jwt()
|
|
||||||
.jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write:filters"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
.andExpect {
|
|
||||||
content {
|
|
||||||
jsonPath("$.keywords[0].keyword") {
|
|
||||||
value("fuga")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `apiV2FiltersPost read権限で401`() {
|
|
||||||
mockMvc
|
|
||||||
.post("/api/v2/filters") {
|
|
||||||
contentType = MediaType.APPLICATION_JSON
|
|
||||||
content = ActivityPubConfig().objectMapper().writeValueAsString(
|
|
||||||
FilterPostRequest(
|
|
||||||
title = "mute test",
|
|
||||||
context = listOf(FilterPostRequest.Context.home, FilterPostRequest.Context.public),
|
|
||||||
filterAction = FilterPostRequest.FilterAction.warn,
|
|
||||||
expiresIn = null,
|
|
||||||
keywordsAttributes = listOf(
|
|
||||||
FilterPostRequestKeyword(
|
|
||||||
keyword = "fuga",
|
|
||||||
wholeWord = true,
|
|
||||||
regex = false
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
with(
|
|
||||||
jwt()
|
|
||||||
.jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.andExpect { status { isForbidden() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `apiV2FiltersGet read権限で取得できる`() {
|
|
||||||
mockMvc
|
|
||||||
.get("/api/v2/filters") {
|
|
||||||
with(
|
|
||||||
jwt()
|
|
||||||
.jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `apiV2FiltersGet read_filters権限で取得できる`() {
|
|
||||||
mockMvc
|
|
||||||
.get("/api/v2/filters") {
|
|
||||||
with(
|
|
||||||
jwt()
|
|
||||||
.jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read:filters"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `apiV2FiltersGet write権限で401`() {
|
|
||||||
mockMvc
|
|
||||||
.get("/api/v2/filters") {
|
|
||||||
with(
|
|
||||||
jwt()
|
|
||||||
.jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.andExpect { status { isForbidden() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `apiV2FiltersIdGet read権限で取得できる`() {
|
|
||||||
mockMvc
|
|
||||||
.get("/api/v2/filters/1") {
|
|
||||||
with(
|
|
||||||
jwt()
|
|
||||||
.jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `apiV2FiltersIdGet read_filters権限で取得できる`() {
|
|
||||||
mockMvc
|
|
||||||
.get("/api/v2/filters/1") {
|
|
||||||
with(
|
|
||||||
jwt()
|
|
||||||
.jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read:filters"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `apiV2FiltersIdGet write権限で401`() {
|
|
||||||
mockMvc
|
|
||||||
.get("/api/v2/filters/1") {
|
|
||||||
with(
|
|
||||||
jwt()
|
|
||||||
.jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.andExpect { status { isForbidden() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `apiV2FiltersFilterIdKeywordsGet read権限で取得できる`() {
|
|
||||||
mockMvc
|
|
||||||
.get("/api/v2/filters/1/keywords") {
|
|
||||||
with(
|
|
||||||
jwt()
|
|
||||||
.jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `apiV2FiltersFilterIdKeywordsGet read_filters権限で取得できる`() {
|
|
||||||
mockMvc
|
|
||||||
.get("/api/v2/filters/1/keywords") {
|
|
||||||
with(
|
|
||||||
jwt()
|
|
||||||
.jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read:filters"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `apiV2FiltersFilterIdKeywordsGet writeで403`() {
|
|
||||||
mockMvc
|
|
||||||
.get("/api/v2/filters/1/keywords") {
|
|
||||||
with(
|
|
||||||
jwt()
|
|
||||||
.jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.andExpect { status { isForbidden() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `apiV2FiltersFilterIdKeywordsPost writeで追加できる`() {
|
|
||||||
mockMvc
|
|
||||||
.post("/api/v2/filters/1/keywords") {
|
|
||||||
contentType = MediaType.APPLICATION_JSON
|
|
||||||
content = ActivityPubConfig().objectMapper().writeValueAsString(
|
|
||||||
FilterKeywordsPostRequest(
|
|
||||||
"hage", false, false
|
|
||||||
)
|
|
||||||
)
|
|
||||||
with(
|
|
||||||
jwt()
|
|
||||||
.jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `apiV2FiltersFilterIdKeywordsPost write_filtersで追加できる`() {
|
|
||||||
mockMvc
|
|
||||||
.post("/api/v2/filters/1/keywords") {
|
|
||||||
contentType = MediaType.APPLICATION_JSON
|
|
||||||
content = ActivityPubConfig().objectMapper().writeValueAsString(
|
|
||||||
FilterKeywordsPostRequest(
|
|
||||||
"hage", false, false
|
|
||||||
)
|
|
||||||
)
|
|
||||||
with(
|
|
||||||
jwt()
|
|
||||||
.jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write:filters"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `apiV2FiltersFilterIdKeywordsPost readで403`() {
|
|
||||||
mockMvc
|
|
||||||
.post("/api/v2/filters/1/keywords") {
|
|
||||||
contentType = MediaType.APPLICATION_JSON
|
|
||||||
content = ActivityPubConfig().objectMapper().writeValueAsString(
|
|
||||||
FilterKeywordsPostRequest(
|
|
||||||
"hage", false, false
|
|
||||||
)
|
|
||||||
)
|
|
||||||
with(
|
|
||||||
jwt()
|
|
||||||
.jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.andExpect { status { isForbidden() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `apiV2FiltersKeywordsIdGet readで取得できる`() {
|
|
||||||
mockMvc
|
|
||||||
.get("/api/v2/filters/keywords/1") {
|
|
||||||
with(
|
|
||||||
jwt()
|
|
||||||
.jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `apiV2FiltersKeywordsIdGet read_filtersで取得できる`() {
|
|
||||||
mockMvc
|
|
||||||
.get("/api/v2/filters/keywords/1") {
|
|
||||||
with(
|
|
||||||
jwt()
|
|
||||||
.jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read:filters"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `apiV2FiltersKeywordsIdGet writeだと403`() {
|
|
||||||
mockMvc
|
|
||||||
.get("/api/v2/filters/keywords/1") {
|
|
||||||
with(
|
|
||||||
jwt()
|
|
||||||
.jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.andExpect { status { isForbidden() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `apiV2FiltersKeyowrdsIdDelete writeで削除できる`() = runTest {
|
|
||||||
mockMvc
|
|
||||||
.delete("/api/v2/filters/keywords/1") {
|
|
||||||
with(
|
|
||||||
jwt()
|
|
||||||
.jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `apiV2FiltersKeyowrdsIdDelete write_filtersで削除できる`() = runTest {
|
|
||||||
mockMvc
|
|
||||||
.delete("/api/v2/filters/keywords/1") {
|
|
||||||
with(
|
|
||||||
jwt()
|
|
||||||
.jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write:filters"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `apiV2FiltersKeyowrdsIdDelete readで403`() = runTest {
|
|
||||||
mockMvc
|
|
||||||
.delete("/api/v2/filters/keywords/1") {
|
|
||||||
with(
|
|
||||||
jwt()
|
|
||||||
.jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.andExpect { status { isForbidden() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `apiV2FiltersFilterIdStatuses readで取得できる`() {
|
|
||||||
mockMvc
|
|
||||||
.get("/api/v2/filters/1/statuses") {
|
|
||||||
with(
|
|
||||||
jwt()
|
|
||||||
.jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `apiV2FiltersFilterIdStatuses read_filtersで取得できる`() {
|
|
||||||
mockMvc
|
|
||||||
.get("/api/v2/filters/1/statuses") {
|
|
||||||
with(
|
|
||||||
jwt()
|
|
||||||
.jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read:filters"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `apiV2FiltersFilterIdStatuses writeで403`() {
|
|
||||||
mockMvc
|
|
||||||
.get("/api/v2/filters/1/statuses") {
|
|
||||||
with(
|
|
||||||
jwt()
|
|
||||||
.jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.andExpect { status { isForbidden() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `apiV2FiltersStatusesIdGet readで取得できる`() {
|
|
||||||
mockMvc
|
|
||||||
.get("/api/v2/filters/statuses/1") {
|
|
||||||
with(
|
|
||||||
jwt()
|
|
||||||
.jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `apiV2FiltersStatusesIdGet read_filtersで取得できる`() {
|
|
||||||
mockMvc
|
|
||||||
.get("/api/v2/filters/statuses/1") {
|
|
||||||
with(
|
|
||||||
jwt()
|
|
||||||
.jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read:filters"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `apiV2FiltersStatusesIdGet writeで403`() {
|
|
||||||
mockMvc
|
|
||||||
.get("/api/v2/filters/statuses/1") {
|
|
||||||
with(
|
|
||||||
jwt()
|
|
||||||
.jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.andExpect { status { isForbidden() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `apiV2FiltersStatusesIdDelete writeで削除できる`() {
|
|
||||||
mockMvc
|
|
||||||
.delete("/api/v2/filters/statuses/1") {
|
|
||||||
with(
|
|
||||||
jwt()
|
|
||||||
.jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `apiV2FiltersStatusesIdDelete write_filtersで削除できる`() {
|
|
||||||
mockMvc
|
|
||||||
.delete("/api/v2/filters/statuses/1") {
|
|
||||||
with(
|
|
||||||
jwt()
|
|
||||||
.jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write:filters"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `apiV2FiltersStatusesIdDelete readで403`() {
|
|
||||||
mockMvc
|
|
||||||
.delete("/api/v2/filters/statuses/1") {
|
|
||||||
with(
|
|
||||||
jwt()
|
|
||||||
.jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.andExpect { status { isForbidden() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `apiV1FiltersGet readで取得できる`() {
|
|
||||||
mockMvc
|
|
||||||
.get("/api/v1/filters") {
|
|
||||||
with(
|
|
||||||
jwt()
|
|
||||||
.jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `apiV1FiltersGet read_filtersで取得できる`() {
|
|
||||||
mockMvc
|
|
||||||
.get("/api/v1/filters") {
|
|
||||||
with(
|
|
||||||
jwt()
|
|
||||||
.jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read:filters"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `apiV1FiltersGet writeで403`() {
|
|
||||||
mockMvc
|
|
||||||
.get("/api/v1/filters") {
|
|
||||||
with(
|
|
||||||
jwt()
|
|
||||||
.jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.andExpect { status { isForbidden() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `apiV1FiltersPost writeで新規作成`() {
|
|
||||||
mockMvc
|
|
||||||
.post("/api/v1/filters") {
|
|
||||||
contentType = MediaType.APPLICATION_JSON
|
|
||||||
content = ActivityPubConfig().objectMapper().writeValueAsString(
|
|
||||||
V1FilterPostRequest(
|
|
||||||
phrase = "hoge",
|
|
||||||
context = listOf(V1FilterPostRequest.Context.home),
|
|
||||||
irreversible = false,
|
|
||||||
wholeWord = false,
|
|
||||||
expiresIn = null
|
|
||||||
)
|
|
||||||
)
|
|
||||||
with(
|
|
||||||
jwt()
|
|
||||||
.jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `apiV1FiltersPost write_filtersで新規作成`() {
|
|
||||||
mockMvc
|
|
||||||
.post("/api/v1/filters") {
|
|
||||||
contentType = MediaType.APPLICATION_JSON
|
|
||||||
content = ActivityPubConfig().objectMapper().writeValueAsString(
|
|
||||||
V1FilterPostRequest(
|
|
||||||
phrase = "hoge",
|
|
||||||
context = listOf(V1FilterPostRequest.Context.home),
|
|
||||||
irreversible = false,
|
|
||||||
wholeWord = false,
|
|
||||||
expiresIn = null
|
|
||||||
)
|
|
||||||
)
|
|
||||||
with(
|
|
||||||
jwt()
|
|
||||||
.jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write:filters"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `apiV1FiltersPost readで403`() {
|
|
||||||
mockMvc
|
|
||||||
.post("/api/v1/filters") {
|
|
||||||
contentType = MediaType.APPLICATION_JSON
|
|
||||||
content = ActivityPubConfig().objectMapper().writeValueAsString(
|
|
||||||
V1FilterPostRequest(
|
|
||||||
phrase = "hoge",
|
|
||||||
context = listOf(V1FilterPostRequest.Context.home),
|
|
||||||
irreversible = false,
|
|
||||||
wholeWord = false,
|
|
||||||
expiresIn = null
|
|
||||||
)
|
|
||||||
)
|
|
||||||
with(
|
|
||||||
jwt()
|
|
||||||
.jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.andExpect { status { isForbidden() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `apiV1FiltersIdGet readで取得できる`() {
|
|
||||||
mockMvc
|
|
||||||
.get("/api/v1/filters/1") {
|
|
||||||
with(
|
|
||||||
jwt().jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `apiV1FiltersIdGet read_filtersで取得できる`() {
|
|
||||||
mockMvc
|
|
||||||
.get("/api/v1/filters/1") {
|
|
||||||
with(
|
|
||||||
jwt().jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read:filters"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `apiV1FiltersIdGet writeで403`() {
|
|
||||||
mockMvc
|
|
||||||
.get("/api/v1/filters/1") {
|
|
||||||
with(
|
|
||||||
jwt().jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.andExpect { status { isForbidden() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `apiV1FiltersIdDelete writeで削除できる`() {
|
|
||||||
mockMvc
|
|
||||||
.delete("/api/v1/filters/1") {
|
|
||||||
with(
|
|
||||||
jwt().jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `apiV1FiltersIdDelete write_filtersで削除できる`() {
|
|
||||||
mockMvc
|
|
||||||
.delete("/api/v1/filters/1") {
|
|
||||||
with(
|
|
||||||
jwt().jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write:filters"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `apiV1FiltersIdDelete readで403`() {
|
|
||||||
mockMvc
|
|
||||||
.delete("/api/v1/filters/1") {
|
|
||||||
with(
|
|
||||||
jwt().jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
@JvmStatic
|
|
||||||
@AfterAll
|
|
||||||
fun dropDatabase(@Autowired flyway: Flyway, @Autowired owlProducer: OwlProducer) {
|
|
||||||
flyway.clean()
|
|
||||||
flyway.migrate()
|
|
||||||
runBlocking {
|
|
||||||
owlProducer.stop()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,145 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package mastodon.media
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.SpringApplication
|
|
||||||
import dev.usbharu.hideout.core.service.media.MediaDataStore
|
|
||||||
import dev.usbharu.hideout.core.service.media.MediaSaveRequest
|
|
||||||
import dev.usbharu.hideout.core.service.media.SuccessSavedMedia
|
|
||||||
import dev.usbharu.owl.producer.api.OwlProducer
|
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import kotlinx.coroutines.test.runTest
|
|
||||||
import org.flywaydb.core.Flyway
|
|
||||||
import org.junit.jupiter.api.AfterAll
|
|
||||||
import org.junit.jupiter.api.BeforeEach
|
|
||||||
import org.junit.jupiter.api.Test
|
|
||||||
import org.mockito.kotlin.any
|
|
||||||
import org.mockito.kotlin.doReturn
|
|
||||||
import org.mockito.kotlin.whenever
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired
|
|
||||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc
|
|
||||||
import org.springframework.boot.test.context.SpringBootTest
|
|
||||||
import org.springframework.boot.test.mock.mockito.MockBean
|
|
||||||
import org.springframework.mock.web.MockMultipartFile
|
|
||||||
import org.springframework.security.core.authority.SimpleGrantedAuthority
|
|
||||||
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.jwt
|
|
||||||
import org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers
|
|
||||||
import org.springframework.test.context.jdbc.Sql
|
|
||||||
import org.springframework.test.web.servlet.MockMvc
|
|
||||||
import org.springframework.test.web.servlet.multipart
|
|
||||||
import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder
|
|
||||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders
|
|
||||||
import org.springframework.transaction.annotation.Transactional
|
|
||||||
import org.springframework.web.context.WebApplicationContext
|
|
||||||
|
|
||||||
@SpringBootTest(classes = [SpringApplication::class])
|
|
||||||
@AutoConfigureMockMvc
|
|
||||||
@Transactional
|
|
||||||
@Sql("/sql/test-user.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_CLASS)
|
|
||||||
class MediaTest {
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private lateinit var context: WebApplicationContext
|
|
||||||
|
|
||||||
private lateinit var mockMvc: MockMvc
|
|
||||||
|
|
||||||
|
|
||||||
@MockBean
|
|
||||||
private lateinit var mediaDataStore: MediaDataStore
|
|
||||||
|
|
||||||
@BeforeEach
|
|
||||||
fun setUp() {
|
|
||||||
mockMvc = MockMvcBuilders.webAppContextSetup(context)
|
|
||||||
.apply<DefaultMockMvcBuilder>(SecurityMockMvcConfigurers.springSecurity())
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun メディアをアップロードできる() = runTest {
|
|
||||||
whenever(mediaDataStore.save(any<MediaSaveRequest>())).doReturn(SuccessSavedMedia("", "a", "a"))
|
|
||||||
|
|
||||||
mockMvc
|
|
||||||
.multipart("/api/v1/media") {
|
|
||||||
|
|
||||||
file(
|
|
||||||
MockMultipartFile(
|
|
||||||
"file",
|
|
||||||
"400x400.png",
|
|
||||||
"image/png",
|
|
||||||
String.javaClass.classLoader.getResourceAsStream("media/400x400.png")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
with(jwt().jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write")))
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun write_mediaスコープでメディアをアップロードできる() = runTest {
|
|
||||||
whenever(mediaDataStore.save(any<MediaSaveRequest>())).doReturn(SuccessSavedMedia("", "b", "b"))
|
|
||||||
|
|
||||||
mockMvc
|
|
||||||
.multipart("/api/v1/media") {
|
|
||||||
|
|
||||||
file(
|
|
||||||
MockMultipartFile(
|
|
||||||
"file",
|
|
||||||
"400x400.png",
|
|
||||||
"image/png",
|
|
||||||
String.javaClass.classLoader.getResourceAsStream("media/400x400.png")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
with(jwt().jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write:media")))
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun 権限がないと403() = runTest {
|
|
||||||
whenever(mediaDataStore.save(any<MediaSaveRequest>())).doReturn(SuccessSavedMedia("", "", ""))
|
|
||||||
|
|
||||||
mockMvc
|
|
||||||
.multipart("/api/v1/media") {
|
|
||||||
|
|
||||||
file(
|
|
||||||
MockMultipartFile(
|
|
||||||
"file",
|
|
||||||
"400x400.png",
|
|
||||||
"image/png",
|
|
||||||
String.javaClass.classLoader.getResourceAsStream("media/400x400.png")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
with(jwt().jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read")))
|
|
||||||
}
|
|
||||||
.andExpect { status { isForbidden() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
@JvmStatic
|
|
||||||
@AfterAll
|
|
||||||
fun dropDatabase(@Autowired flyway: Flyway, @Autowired owlProducer: OwlProducer) {
|
|
||||||
flyway.clean()
|
|
||||||
flyway.migrate()
|
|
||||||
runBlocking {
|
|
||||||
owlProducer.stop()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,185 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package mastodon.notifications
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.type.TypeReference
|
|
||||||
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
|
||||||
import dev.usbharu.hideout.SpringApplication
|
|
||||||
import dev.usbharu.hideout.domain.mastodon.model.generated.Notification
|
|
||||||
import dev.usbharu.owl.producer.api.OwlProducer
|
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import kotlinx.coroutines.test.runTest
|
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
|
||||||
import org.flywaydb.core.Flyway
|
|
||||||
import org.junit.jupiter.api.AfterAll
|
|
||||||
import org.junit.jupiter.api.BeforeEach
|
|
||||||
import org.junit.jupiter.api.Test
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired
|
|
||||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc
|
|
||||||
import org.springframework.boot.test.context.SpringBootTest
|
|
||||||
import org.springframework.security.core.authority.SimpleGrantedAuthority
|
|
||||||
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors
|
|
||||||
import org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers
|
|
||||||
import org.springframework.test.context.jdbc.Sql
|
|
||||||
import org.springframework.test.web.servlet.MockMvc
|
|
||||||
import org.springframework.test.web.servlet.get
|
|
||||||
import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder
|
|
||||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders
|
|
||||||
import org.springframework.transaction.annotation.Transactional
|
|
||||||
import org.springframework.web.context.WebApplicationContext
|
|
||||||
|
|
||||||
@SpringBootTest(classes = [SpringApplication::class], properties = ["hideout.use-mongodb=false"])
|
|
||||||
@AutoConfigureMockMvc
|
|
||||||
@Transactional
|
|
||||||
@Sql("/sql/test-user.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_CLASS)
|
|
||||||
@Sql("/sql/test-user2.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_CLASS)
|
|
||||||
@Sql("/sql/notification/test-notifications.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_CLASS)
|
|
||||||
@Sql("/sql/notification/test-mastodon_notifications.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_CLASS)
|
|
||||||
class ExposedNotificationsApiPaginationTest {
|
|
||||||
@Autowired
|
|
||||||
private lateinit var context: WebApplicationContext
|
|
||||||
|
|
||||||
private lateinit var mockMvc: MockMvc
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `通知を取得できる`() = runTest {
|
|
||||||
val content = mockMvc
|
|
||||||
.get("/api/v1/notifications") {
|
|
||||||
with(
|
|
||||||
SecurityMockMvcRequestPostProcessors.jwt()
|
|
||||||
.jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect {
|
|
||||||
header {
|
|
||||||
string(
|
|
||||||
"Link",
|
|
||||||
"<https://example.com/api/v1/notifications?min_id=65>; rel=\"next\", <https://example.com/api/v1/notifications?max_id=26>; rel=\"prev\""
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.andReturn()
|
|
||||||
.response
|
|
||||||
.contentAsString
|
|
||||||
|
|
||||||
val value = jacksonObjectMapper().readValue(content, object : TypeReference<List<Notification>>() {})
|
|
||||||
|
|
||||||
assertThat(value.first().id).isEqualTo("65")
|
|
||||||
assertThat(value.last().id).isEqualTo("26")
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun maxIdを指定して通知を取得できる() = runTest {
|
|
||||||
val content = mockMvc
|
|
||||||
.get("/api/v1/notifications?max_id=26") {
|
|
||||||
with(
|
|
||||||
SecurityMockMvcRequestPostProcessors.jwt()
|
|
||||||
.jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect {
|
|
||||||
header {
|
|
||||||
string(
|
|
||||||
"Link",
|
|
||||||
"<https://example.com/api/v1/notifications?min_id=25>; rel=\"next\", <https://example.com/api/v1/notifications?max_id=1>; rel=\"prev\""
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.andReturn()
|
|
||||||
.response
|
|
||||||
.contentAsString
|
|
||||||
|
|
||||||
val value = jacksonObjectMapper().readValue(content, object : TypeReference<List<Notification>>() {})
|
|
||||||
|
|
||||||
assertThat(value.first().id).isEqualTo("25")
|
|
||||||
assertThat(value.last().id).isEqualTo("1")
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun minIdを指定して通知を取得できる() = runTest {
|
|
||||||
val content = mockMvc
|
|
||||||
.get("/api/v1/notifications?min_id=25") {
|
|
||||||
with(
|
|
||||||
SecurityMockMvcRequestPostProcessors.jwt()
|
|
||||||
.jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect {
|
|
||||||
header {
|
|
||||||
string(
|
|
||||||
"Link",
|
|
||||||
"<https://example.com/api/v1/notifications?min_id=65>; rel=\"next\", <https://example.com/api/v1/notifications?max_id=26>; rel=\"prev\""
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.andReturn()
|
|
||||||
.response
|
|
||||||
.contentAsString
|
|
||||||
|
|
||||||
val value = jacksonObjectMapper().readValue(content, object : TypeReference<List<Notification>>() {})
|
|
||||||
|
|
||||||
assertThat(value.first().id).isEqualTo("65")
|
|
||||||
assertThat(value.last().id).isEqualTo("26")
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun 結果が0件のときはページネーションのヘッダーがない() = runTest {
|
|
||||||
val content = mockMvc
|
|
||||||
.get("/api/v1/notifications?max_id=1") {
|
|
||||||
with(
|
|
||||||
SecurityMockMvcRequestPostProcessors.jwt()
|
|
||||||
.jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect {
|
|
||||||
header {
|
|
||||||
doesNotExist("Link")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.andReturn()
|
|
||||||
.response
|
|
||||||
.contentAsString
|
|
||||||
|
|
||||||
val value = jacksonObjectMapper().readValue(content, object : TypeReference<List<Notification>>() {})
|
|
||||||
|
|
||||||
assertThat(value).size().isZero()
|
|
||||||
}
|
|
||||||
|
|
||||||
@BeforeEach
|
|
||||||
fun setUp() {
|
|
||||||
mockMvc = MockMvcBuilders.webAppContextSetup(context)
|
|
||||||
.apply<DefaultMockMvcBuilder>(SecurityMockMvcConfigurers.springSecurity())
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
@JvmStatic
|
|
||||||
@AfterAll
|
|
||||||
fun dropDatabase(@Autowired flyway: Flyway, @Autowired owlProducer: OwlProducer) {
|
|
||||||
flyway.clean()
|
|
||||||
flyway.migrate()
|
|
||||||
runBlocking {
|
|
||||||
owlProducer.stop()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,219 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package mastodon.notifications
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.type.TypeReference
|
|
||||||
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
|
||||||
import dev.usbharu.hideout.SpringApplication
|
|
||||||
import dev.usbharu.hideout.domain.mastodon.model.generated.Notification
|
|
||||||
import dev.usbharu.hideout.mastodon.domain.model.MastodonNotification
|
|
||||||
import dev.usbharu.hideout.mastodon.domain.model.NotificationType
|
|
||||||
import dev.usbharu.hideout.mastodon.infrastructure.mongorepository.MongoMastodonNotificationRepository
|
|
||||||
import dev.usbharu.owl.producer.api.OwlProducer
|
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import kotlinx.coroutines.test.runTest
|
|
||||||
import org.assertj.core.api.Assertions
|
|
||||||
import org.flywaydb.core.Flyway
|
|
||||||
import org.junit.jupiter.api.AfterAll
|
|
||||||
import org.junit.jupiter.api.BeforeAll
|
|
||||||
import org.junit.jupiter.api.BeforeEach
|
|
||||||
import org.junit.jupiter.api.Test
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired
|
|
||||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc
|
|
||||||
import org.springframework.boot.test.context.SpringBootTest
|
|
||||||
import org.springframework.security.core.authority.SimpleGrantedAuthority
|
|
||||||
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors
|
|
||||||
import org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers
|
|
||||||
import org.springframework.test.context.jdbc.Sql
|
|
||||||
import org.springframework.test.web.servlet.MockMvc
|
|
||||||
import org.springframework.test.web.servlet.get
|
|
||||||
import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder
|
|
||||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders
|
|
||||||
import org.springframework.transaction.annotation.Transactional
|
|
||||||
import org.springframework.web.context.WebApplicationContext
|
|
||||||
import java.time.Instant
|
|
||||||
|
|
||||||
@SpringBootTest(classes = [SpringApplication::class], properties = ["hideout.use-mongodb=true"])
|
|
||||||
@AutoConfigureMockMvc
|
|
||||||
@Transactional
|
|
||||||
@Sql("/sql/test-user.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_CLASS)
|
|
||||||
@Sql("/sql/test-user2.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_CLASS)
|
|
||||||
@Sql("/sql/notification/test-notifications.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_CLASS)
|
|
||||||
class MongodbNotificationsApiPaginationTest {
|
|
||||||
@Autowired
|
|
||||||
private lateinit var context: WebApplicationContext
|
|
||||||
|
|
||||||
private lateinit var mockMvc: MockMvc
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `通知を取得できる`() = runTest {
|
|
||||||
val content = mockMvc
|
|
||||||
.get("/api/v1/notifications") {
|
|
||||||
with(
|
|
||||||
SecurityMockMvcRequestPostProcessors.jwt()
|
|
||||||
.jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andDo { print() }
|
|
||||||
.andExpect {
|
|
||||||
header {
|
|
||||||
string(
|
|
||||||
"Link",
|
|
||||||
"<https://example.com/api/v1/notifications?min_id=65>; rel=\"next\", <https://example.com/api/v1/notifications?max_id=26>; rel=\"prev\""
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.andReturn()
|
|
||||||
.response
|
|
||||||
.contentAsString
|
|
||||||
|
|
||||||
val value = jacksonObjectMapper().readValue(content, object : TypeReference<List<Notification>>() {})
|
|
||||||
|
|
||||||
Assertions.assertThat(value.first().id).isEqualTo("65")
|
|
||||||
Assertions.assertThat(value.last().id).isEqualTo("26")
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun maxIdを指定して通知を取得できる() = runTest {
|
|
||||||
val content = mockMvc
|
|
||||||
.get("/api/v1/notifications?max_id=26") {
|
|
||||||
with(
|
|
||||||
SecurityMockMvcRequestPostProcessors.jwt()
|
|
||||||
.jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect {
|
|
||||||
header {
|
|
||||||
string(
|
|
||||||
"Link",
|
|
||||||
"<https://example.com/api/v1/notifications?min_id=25>; rel=\"next\", <https://example.com/api/v1/notifications?max_id=1>; rel=\"prev\""
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.andReturn()
|
|
||||||
.response
|
|
||||||
.contentAsString
|
|
||||||
|
|
||||||
val value = jacksonObjectMapper().readValue(content, object : TypeReference<List<Notification>>() {})
|
|
||||||
|
|
||||||
Assertions.assertThat(value.first().id).isEqualTo("25")
|
|
||||||
Assertions.assertThat(value.last().id).isEqualTo("1")
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun minIdを指定して通知を取得できる() = runTest {
|
|
||||||
val content = mockMvc
|
|
||||||
.get("/api/v1/notifications?min_id=25") {
|
|
||||||
with(
|
|
||||||
SecurityMockMvcRequestPostProcessors.jwt()
|
|
||||||
.jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect {
|
|
||||||
header {
|
|
||||||
string(
|
|
||||||
"Link",
|
|
||||||
"<https://example.com/api/v1/notifications?min_id=65>; rel=\"next\", <https://example.com/api/v1/notifications?max_id=26>; rel=\"prev\""
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.andReturn()
|
|
||||||
.response
|
|
||||||
.contentAsString
|
|
||||||
|
|
||||||
val value = jacksonObjectMapper().readValue(content, object : TypeReference<List<Notification>>() {})
|
|
||||||
|
|
||||||
Assertions.assertThat(value.first().id).isEqualTo("65")
|
|
||||||
Assertions.assertThat(value.last().id).isEqualTo("26")
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun 結果が0件のときはページネーションのヘッダーがない() = runTest {
|
|
||||||
val content = mockMvc
|
|
||||||
.get("/api/v1/notifications?max_id=1") {
|
|
||||||
with(
|
|
||||||
SecurityMockMvcRequestPostProcessors.jwt()
|
|
||||||
.jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect {
|
|
||||||
header {
|
|
||||||
doesNotExist("Link")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.andReturn()
|
|
||||||
.response
|
|
||||||
.contentAsString
|
|
||||||
|
|
||||||
val value = jacksonObjectMapper().readValue(content, object : TypeReference<List<Notification>>() {})
|
|
||||||
|
|
||||||
Assertions.assertThat(value).size().isZero()
|
|
||||||
}
|
|
||||||
|
|
||||||
@BeforeEach
|
|
||||||
fun setUp() {
|
|
||||||
mockMvc = MockMvcBuilders.webAppContextSetup(context)
|
|
||||||
.apply<DefaultMockMvcBuilder>(SecurityMockMvcConfigurers.springSecurity())
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
@JvmStatic
|
|
||||||
@BeforeAll
|
|
||||||
fun setupMongodb(
|
|
||||||
@Autowired mongoMastodonNotificationRepository: MongoMastodonNotificationRepository,
|
|
||||||
) {
|
|
||||||
|
|
||||||
mongoMastodonNotificationRepository.deleteAll()
|
|
||||||
|
|
||||||
val notifications = (1..65).map {
|
|
||||||
MastodonNotification(
|
|
||||||
it.toLong(),
|
|
||||||
1,
|
|
||||||
NotificationType.follow,
|
|
||||||
Instant.now(),
|
|
||||||
2,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
mongoMastodonNotificationRepository.saveAll(notifications)
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
@AfterAll
|
|
||||||
fun dropDatabase(
|
|
||||||
@Autowired flyway: Flyway,
|
|
||||||
@Autowired mongodbMastodonNotificationRepository: MongoMastodonNotificationRepository,
|
|
||||||
@Autowired owlProducer: OwlProducer,
|
|
||||||
) {
|
|
||||||
flyway.clean()
|
|
||||||
flyway.migrate()
|
|
||||||
runBlocking {
|
|
||||||
owlProducer.stop()
|
|
||||||
}
|
|
||||||
mongodbMastodonNotificationRepository.deleteAll()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,249 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package mastodon.status
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.SpringApplication
|
|
||||||
import dev.usbharu.hideout.core.domain.model.emoji.CustomEmoji
|
|
||||||
import dev.usbharu.hideout.core.domain.model.emoji.UnicodeEmoji
|
|
||||||
import dev.usbharu.hideout.core.infrastructure.exposedrepository.CustomEmojis
|
|
||||||
import dev.usbharu.hideout.core.infrastructure.exposedrepository.Reactions
|
|
||||||
import dev.usbharu.hideout.core.infrastructure.exposedrepository.toReaction
|
|
||||||
import dev.usbharu.owl.producer.api.OwlProducer
|
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
|
||||||
import org.flywaydb.core.Flyway
|
|
||||||
import org.jetbrains.exposed.sql.and
|
|
||||||
import org.jetbrains.exposed.sql.selectAll
|
|
||||||
import org.junit.jupiter.api.AfterAll
|
|
||||||
import org.junit.jupiter.api.BeforeEach
|
|
||||||
import org.junit.jupiter.api.Test
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired
|
|
||||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc
|
|
||||||
import org.springframework.boot.test.context.SpringBootTest
|
|
||||||
import org.springframework.http.MediaType
|
|
||||||
import org.springframework.security.core.authority.SimpleGrantedAuthority
|
|
||||||
import org.springframework.security.test.context.support.WithAnonymousUser
|
|
||||||
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf
|
|
||||||
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.jwt
|
|
||||||
import org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers
|
|
||||||
import org.springframework.test.context.jdbc.Sql
|
|
||||||
import org.springframework.test.web.servlet.MockMvc
|
|
||||||
import org.springframework.test.web.servlet.post
|
|
||||||
import org.springframework.test.web.servlet.put
|
|
||||||
import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder
|
|
||||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders
|
|
||||||
import org.springframework.transaction.annotation.Transactional
|
|
||||||
import org.springframework.web.context.WebApplicationContext
|
|
||||||
import java.time.Instant
|
|
||||||
|
|
||||||
@SpringBootTest(classes = [SpringApplication::class])
|
|
||||||
@AutoConfigureMockMvc
|
|
||||||
@Transactional
|
|
||||||
@Sql("/sql/test-user.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_CLASS)
|
|
||||||
@Sql("/sql/test-post.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_CLASS)
|
|
||||||
@Sql("/sql/test-custom-emoji.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_CLASS)
|
|
||||||
class StatusTest {
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private lateinit var context: WebApplicationContext
|
|
||||||
|
|
||||||
private lateinit var mockMvc: MockMvc
|
|
||||||
|
|
||||||
|
|
||||||
@BeforeEach
|
|
||||||
fun setUp() {
|
|
||||||
mockMvc = MockMvcBuilders.webAppContextSetup(context)
|
|
||||||
.apply<DefaultMockMvcBuilder>(SecurityMockMvcConfigurers.springSecurity())
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun 投稿できる() {
|
|
||||||
mockMvc
|
|
||||||
.post("/api/v1/statuses") {
|
|
||||||
contentType = MediaType.APPLICATION_JSON
|
|
||||||
content = """{"status":"hello"}"""
|
|
||||||
with(
|
|
||||||
jwt()
|
|
||||||
.jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun write_statusesスコープで投稿できる() {
|
|
||||||
mockMvc
|
|
||||||
.post("/api/v1/statuses") {
|
|
||||||
contentType = MediaType.APPLICATION_JSON
|
|
||||||
content = """{"status":"hello"}"""
|
|
||||||
with(
|
|
||||||
jwt()
|
|
||||||
.jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write:statuses"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun 権限がないと403() {
|
|
||||||
mockMvc
|
|
||||||
.post("/api/v1/statuses") {
|
|
||||||
contentType = MediaType.APPLICATION_JSON
|
|
||||||
content = """{"status":"hello"}"""
|
|
||||||
with(
|
|
||||||
jwt()
|
|
||||||
.jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.andExpect { status { isForbidden() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@WithAnonymousUser
|
|
||||||
fun 匿名だと401() {
|
|
||||||
mockMvc
|
|
||||||
.post("/api/v1/statuses") {
|
|
||||||
contentType = MediaType.APPLICATION_JSON
|
|
||||||
content = """{"status":"hello"}"""
|
|
||||||
with(csrf())
|
|
||||||
}
|
|
||||||
.andExpect { status { isUnauthorized() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@WithAnonymousUser
|
|
||||||
fun 匿名の場合通常はcsrfが無いので403() {
|
|
||||||
mockMvc
|
|
||||||
.post("/api/v1/statuses") {
|
|
||||||
contentType = MediaType.APPLICATION_JSON
|
|
||||||
content = """{"status":"hello"}"""
|
|
||||||
}
|
|
||||||
.andExpect { status { isForbidden() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun formでも投稿できる() {
|
|
||||||
mockMvc
|
|
||||||
.post("/api/v1/statuses") {
|
|
||||||
contentType = MediaType.APPLICATION_FORM_URLENCODED
|
|
||||||
param("status", "hello")
|
|
||||||
with(
|
|
||||||
jwt()
|
|
||||||
.jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write:statuses"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun in_reply_to_idを指定したら返信として処理される() {
|
|
||||||
mockMvc
|
|
||||||
.post("/api/v1/statuses") {
|
|
||||||
contentType = MediaType.APPLICATION_JSON
|
|
||||||
//language=JSON
|
|
||||||
content = """{
|
|
||||||
"status": "hello",
|
|
||||||
"in_reply_to_id": "1"
|
|
||||||
}"""
|
|
||||||
with(
|
|
||||||
jwt()
|
|
||||||
.jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andDo { print() }
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
.andExpect { jsonPath("\$.in_reply_to_id") { value("1") } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun ユニコード絵文字をリアクションできる() {
|
|
||||||
mockMvc
|
|
||||||
.put("/api/v1/statuses/1/emoji_reactions/😭") {
|
|
||||||
with(jwt().jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write")))
|
|
||||||
}
|
|
||||||
.andDo { print() }
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
|
|
||||||
val reaction =
|
|
||||||
Reactions.selectAll().where { Reactions.postId eq 1 and (Reactions.actorId eq 1) }.single().toReaction()
|
|
||||||
assertThat(reaction.emoji).isEqualTo(UnicodeEmoji("😭"))
|
|
||||||
assertThat(reaction.postId).isEqualTo(1)
|
|
||||||
assertThat(reaction.actorId).isEqualTo(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun 存在しない絵文字はフォールバックされる() {
|
|
||||||
mockMvc
|
|
||||||
.put("/api/v1/statuses/1/emoji_reactions/hoge") {
|
|
||||||
with(jwt().jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write")))
|
|
||||||
}
|
|
||||||
.andDo { print() }
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
|
|
||||||
val reaction =
|
|
||||||
Reactions.selectAll().where { Reactions.postId eq 1 and (Reactions.actorId eq 1) }.single().toReaction()
|
|
||||||
assertThat(reaction.emoji).isEqualTo(UnicodeEmoji("❤"))
|
|
||||||
assertThat(reaction.postId).isEqualTo(1)
|
|
||||||
assertThat(reaction.actorId).isEqualTo(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun カスタム絵文字をリアクションできる() {
|
|
||||||
mockMvc
|
|
||||||
.put("/api/v1/statuses/1/emoji_reactions/kotlin") {
|
|
||||||
with(jwt().jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write")))
|
|
||||||
}
|
|
||||||
.andDo { print() }
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
|
|
||||||
val reaction =
|
|
||||||
Reactions.leftJoin(CustomEmojis).selectAll().where { Reactions.postId eq 1 and (Reactions.actorId eq 1) }
|
|
||||||
.single()
|
|
||||||
.toReaction()
|
|
||||||
assertThat(reaction.emoji).isEqualTo(
|
|
||||||
CustomEmoji(
|
|
||||||
1,
|
|
||||||
"kotlin",
|
|
||||||
"example.com",
|
|
||||||
null,
|
|
||||||
"https://example.com/emojis/kotlin",
|
|
||||||
null,
|
|
||||||
Instant.ofEpochMilli(1704700290036)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
@JvmStatic
|
|
||||||
@AfterAll
|
|
||||||
fun dropDatabase(@Autowired flyway: Flyway, @Autowired owlProducer: OwlProducer) {
|
|
||||||
flyway.clean()
|
|
||||||
flyway.migrate()
|
|
||||||
runBlocking {
|
|
||||||
owlProducer.stop()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,136 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package mastodon.timelines
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.SpringApplication
|
|
||||||
import dev.usbharu.owl.producer.api.OwlProducer
|
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import org.flywaydb.core.Flyway
|
|
||||||
import org.junit.jupiter.api.AfterAll
|
|
||||||
import org.junit.jupiter.api.BeforeEach
|
|
||||||
import org.junit.jupiter.api.Test
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired
|
|
||||||
import org.springframework.boot.test.context.SpringBootTest
|
|
||||||
import org.springframework.security.core.authority.SimpleGrantedAuthority
|
|
||||||
import org.springframework.security.test.context.support.WithAnonymousUser
|
|
||||||
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors
|
|
||||||
import org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers
|
|
||||||
import org.springframework.test.context.jdbc.Sql
|
|
||||||
import org.springframework.test.web.servlet.MockMvc
|
|
||||||
import org.springframework.test.web.servlet.get
|
|
||||||
import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder
|
|
||||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders
|
|
||||||
import org.springframework.transaction.annotation.Transactional
|
|
||||||
import org.springframework.web.context.WebApplicationContext
|
|
||||||
|
|
||||||
@SpringBootTest(classes = [SpringApplication::class])
|
|
||||||
@Transactional
|
|
||||||
@Sql("/sql/test-user.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_CLASS)
|
|
||||||
class TimelineApiTest {
|
|
||||||
@Autowired
|
|
||||||
private lateinit var context: WebApplicationContext
|
|
||||||
|
|
||||||
private lateinit var mockMvc: MockMvc
|
|
||||||
|
|
||||||
@BeforeEach
|
|
||||||
fun beforeEach() {
|
|
||||||
mockMvc = MockMvcBuilders.webAppContextSetup(context)
|
|
||||||
.apply<DefaultMockMvcBuilder>(SecurityMockMvcConfigurers.springSecurity())
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `apiV1TimelinesHomeGetにreadでアクセスできる`() {
|
|
||||||
mockMvc
|
|
||||||
.get("/api/v1/timelines/home") {
|
|
||||||
with(
|
|
||||||
SecurityMockMvcRequestPostProcessors.jwt()
|
|
||||||
.jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `apiV1TimelinesHomeGetにread statusesでアクセスできる`() {
|
|
||||||
mockMvc
|
|
||||||
.get("/api/v1/timelines/home") {
|
|
||||||
with(
|
|
||||||
SecurityMockMvcRequestPostProcessors.jwt()
|
|
||||||
.jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read:statuses"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@WithAnonymousUser
|
|
||||||
fun apiV1TimelineHomeGetに匿名でアクセスすると401() {
|
|
||||||
mockMvc
|
|
||||||
.get("/api/v1/timelines/home")
|
|
||||||
.andExpect { status { isUnauthorized() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun apiV1TimelinesPublicGetにreadでアクセスできる() {
|
|
||||||
mockMvc
|
|
||||||
.get("/api/v1/timelines/public") {
|
|
||||||
with(
|
|
||||||
SecurityMockMvcRequestPostProcessors.jwt()
|
|
||||||
.jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `apiV1TimelinesPublicGetにread statusesでアクセスできる`() {
|
|
||||||
mockMvc
|
|
||||||
.get("/api/v1/timelines/public") {
|
|
||||||
with(
|
|
||||||
SecurityMockMvcRequestPostProcessors.jwt()
|
|
||||||
.jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read:statuses"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@WithAnonymousUser
|
|
||||||
fun apiV1TimeinesPublicGetに匿名でアクセスできる() {
|
|
||||||
mockMvc
|
|
||||||
.get("/api/v1/timelines/public")
|
|
||||||
.asyncDispatch()
|
|
||||||
.andExpect { status { isOk() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
@JvmStatic
|
|
||||||
@AfterAll
|
|
||||||
fun dropDatabase(@Autowired flyway: Flyway, @Autowired owlProducer: OwlProducer) {
|
|
||||||
flyway.clean()
|
|
||||||
flyway.migrate()
|
|
||||||
runBlocking {
|
|
||||||
owlProducer.stop()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,25 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package util
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.application.external.Transaction
|
|
||||||
|
|
||||||
object TestTransaction : Transaction {
|
|
||||||
override suspend fun <T> transaction(block: suspend () -> T): T = block()
|
|
||||||
|
|
||||||
override suspend fun <T> transaction(transactionLevel: Int, block: suspend () -> T): T = block()
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package util
|
|
||||||
|
|
||||||
import org.springframework.core.annotation.AliasFor
|
|
||||||
import org.springframework.security.test.context.support.TestExecutionEvent
|
|
||||||
import org.springframework.security.test.context.support.WithSecurityContext
|
|
||||||
import java.lang.annotation.Inherited
|
|
||||||
|
|
||||||
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.TYPE)
|
|
||||||
@Retention(AnnotationRetention.RUNTIME)
|
|
||||||
@Inherited
|
|
||||||
@MustBeDocumented
|
|
||||||
@WithSecurityContext(factory = WithHttpSignatureSecurityContextFactory::class)
|
|
||||||
annotation class WithHttpSignature(
|
|
||||||
@get:AliasFor(
|
|
||||||
annotation = WithSecurityContext::class
|
|
||||||
) val setupBefore: TestExecutionEvent = TestExecutionEvent.TEST_METHOD,
|
|
||||||
val keyId: String = "https://example.com/users/test-user#pubkey",
|
|
||||||
val url: String = "https://example.com/inbox",
|
|
||||||
val method: String = "GET"
|
|
||||||
)
|
|
|
@ -1,66 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package util
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.application.external.Transaction
|
|
||||||
import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException
|
|
||||||
import dev.usbharu.hideout.core.domain.model.actor.ActorRepository
|
|
||||||
import dev.usbharu.hideout.core.infrastructure.springframework.httpsignature.HttpSignatureUser
|
|
||||||
import dev.usbharu.httpsignature.common.HttpHeaders
|
|
||||||
import dev.usbharu.httpsignature.common.HttpMethod
|
|
||||||
import dev.usbharu.httpsignature.common.HttpRequest
|
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import org.springframework.security.core.context.SecurityContext
|
|
||||||
import org.springframework.security.core.context.SecurityContextHolder
|
|
||||||
import org.springframework.security.test.context.support.WithSecurityContextFactory
|
|
||||||
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken
|
|
||||||
import java.net.URL
|
|
||||||
|
|
||||||
class WithHttpSignatureSecurityContextFactory(
|
|
||||||
private val actorRepository: ActorRepository,
|
|
||||||
private val transaction: Transaction
|
|
||||||
) : WithSecurityContextFactory<WithHttpSignature> {
|
|
||||||
|
|
||||||
private val securityContextStrategy = SecurityContextHolder.getContextHolderStrategy()
|
|
||||||
|
|
||||||
override fun createSecurityContext(annotation: WithHttpSignature): SecurityContext = runBlocking {
|
|
||||||
val preAuthenticatedAuthenticationToken = PreAuthenticatedAuthenticationToken(
|
|
||||||
annotation.keyId, HttpRequest(
|
|
||||||
URL("https://example.com/inbox"),
|
|
||||||
HttpHeaders(mapOf()), HttpMethod.GET
|
|
||||||
)
|
|
||||||
)
|
|
||||||
val httpSignatureUser = transaction.transaction {
|
|
||||||
val findByKeyId =
|
|
||||||
actorRepository.findByKeyId(annotation.keyId) ?: throw UserNotFoundException.withKeyId(annotation.keyId)
|
|
||||||
HttpSignatureUser(
|
|
||||||
findByKeyId.name,
|
|
||||||
findByKeyId.domain,
|
|
||||||
findByKeyId.id,
|
|
||||||
true,
|
|
||||||
true,
|
|
||||||
mutableListOf()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
preAuthenticatedAuthenticationToken.details = httpSignatureUser
|
|
||||||
preAuthenticatedAuthenticationToken.isAuthenticated = true
|
|
||||||
val emptyContext = securityContextStrategy.createEmptyContext()
|
|
||||||
emptyContext.authentication = preAuthenticatedAuthenticationToken
|
|
||||||
return@runBlocking emptyContext
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,39 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package util
|
|
||||||
|
|
||||||
import org.springframework.core.annotation.AliasFor
|
|
||||||
import org.springframework.security.test.context.support.TestExecutionEvent
|
|
||||||
import org.springframework.security.test.context.support.WithSecurityContext
|
|
||||||
import java.lang.annotation.Inherited
|
|
||||||
|
|
||||||
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.TYPE)
|
|
||||||
@Retention(AnnotationRetention.RUNTIME)
|
|
||||||
@Inherited
|
|
||||||
@MustBeDocumented
|
|
||||||
@WithSecurityContext(factory = WithMockHttpSignatureSecurityContextFactory::class)
|
|
||||||
annotation class WithMockHttpSignature(
|
|
||||||
@get:AliasFor(
|
|
||||||
annotation = WithSecurityContext::class
|
|
||||||
) val setupBefore: TestExecutionEvent = TestExecutionEvent.TEST_METHOD,
|
|
||||||
val username: String = "test-user",
|
|
||||||
val domain: String = "example.com",
|
|
||||||
val keyId: String = "https://example.com/users/test-user#pubkey",
|
|
||||||
val id: Long = 1234L,
|
|
||||||
val url: String = "https://example.com/inbox",
|
|
||||||
val method: String = "GET"
|
|
||||||
)
|
|
|
@ -1,55 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package util
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.core.infrastructure.springframework.httpsignature.HttpSignatureUser
|
|
||||||
import dev.usbharu.httpsignature.common.HttpHeaders
|
|
||||||
import dev.usbharu.httpsignature.common.HttpMethod
|
|
||||||
import dev.usbharu.httpsignature.common.HttpRequest
|
|
||||||
import org.springframework.security.core.context.SecurityContext
|
|
||||||
import org.springframework.security.core.context.SecurityContextHolder
|
|
||||||
import org.springframework.security.test.context.support.WithSecurityContextFactory
|
|
||||||
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken
|
|
||||||
import java.net.URL
|
|
||||||
|
|
||||||
class WithMockHttpSignatureSecurityContextFactory :
|
|
||||||
WithSecurityContextFactory<WithMockHttpSignature> {
|
|
||||||
|
|
||||||
private val securityContextStrategy = SecurityContextHolder.getContextHolderStrategy()
|
|
||||||
|
|
||||||
override fun createSecurityContext(annotation: WithMockHttpSignature): SecurityContext {
|
|
||||||
val preAuthenticatedAuthenticationToken = PreAuthenticatedAuthenticationToken(
|
|
||||||
annotation.keyId, HttpRequest(
|
|
||||||
URL(annotation.url),
|
|
||||||
HttpHeaders(mapOf()), HttpMethod.valueOf(annotation.method.uppercase())
|
|
||||||
)
|
|
||||||
)
|
|
||||||
val httpSignatureUser = HttpSignatureUser(
|
|
||||||
annotation.username,
|
|
||||||
annotation.domain,
|
|
||||||
annotation.id,
|
|
||||||
true,
|
|
||||||
true,
|
|
||||||
mutableListOf()
|
|
||||||
)
|
|
||||||
preAuthenticatedAuthenticationToken.details = httpSignatureUser
|
|
||||||
preAuthenticatedAuthenticationToken.isAuthenticated = true
|
|
||||||
val emptyContext = securityContextStrategy.createEmptyContext()
|
|
||||||
emptyContext.authentication = preAuthenticatedAuthenticationToken
|
|
||||||
return emptyContext
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,40 +0,0 @@
|
||||||
hideout:
|
|
||||||
debug:
|
|
||||||
trace-query-exception: true
|
|
||||||
trace-query-call: true
|
|
||||||
url: "https://example.com"
|
|
||||||
use-mongodb: true
|
|
||||||
security:
|
|
||||||
jwt:
|
|
||||||
generate: true
|
|
||||||
key-id: a
|
|
||||||
private-key: "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC7VJTUt9Us8cKjMzEfYyjiWA4R4/M2bS1GB4t7NXp98C3SC6dVMvDuictGeurT8jNbvJZHtCSuYEvuNMoSfm76oqFvAp8Gy0iz5sxjZmSnXyCdPEovGhLa0VzMaQ8s+CLOyS56YyCFGeJZqgtzJ6GR3eqoYSW9b9UMvkBpZODSctWSNGj3P7jRFDO5VoTwCQAWbFnOjDfH5Ulgp2PKSQnSJP3AJLQNFNe7br1XbrhV//eO+t51mIpGSDCUv3E0DDFcWDTH9cXDTTlRZVEiR2BwpZOOkE/Z0/BVnhZYL71oZV34bKfWjQIt6V/isSMahdsAASACp4ZTGtwiVuNd9tybAgMBAAECggEBAKTmjaS6tkK8BlPXClTQ2vpz/N6uxDeS35mXpqasqskVlaAidgg/sWqpjXDbXr93otIMLlWsM+X0CqMDgSXKejLS2jx4GDjI1ZTXg++0AMJ8sJ74pWzVDOfmCEQ/7wXs3+cbnXhKriO8Z036q92Qc1+N87SI38nkGa0ABH9CN83HmQqt4fB7UdHzuIRe/me2PGhIq5ZBzj6h3BpoPGzEP+x3l9YmK8t/1cN0pqI+dQwYdgfGjackLu/2qH80MCF7IyQaseZUOJyKrCLtSD/Iixv/hzDEUPfOCjFDgTpzf3cwta8+oE4wHCo1iI1/4TlPkwmXx4qSXtmw4aQPz7IDQvECgYEA8KNThCO2gsC2I9PQDM/8Cw0O983WCDY+oi+7JPiNAJwv5DYBqEZB1QYdj06YD16XlC/HAZMsMku1na2TN0driwenQQWzoev3g2S7gRDoS/FCJSI3jJ+kjgtaA7Qmzlgk1TxODN+G1H91HW7t0l7VnL27IWyYo2qRRK3jzxqUiPUCgYEAx0oQs2reBQGMVZnApD1jeq7n4MvNLcPvt8b/eU9iUv6Y4Mj0Suo/AU8lYZXm8ubbqAlwz2VSVunD2tOplHyMUrtCtObAfVDUAhCndKaA9gApgfb3xw1IKbuQ1u4IF1FJl3VtumfQn//LiH1B3rXhcdyo3/vIttEk48RakUKClU8CgYEAzV7W3COOlDDcQd935DdtKBFRAPRPAlspQUnzMi5eSHMD/ISLDY5IiQHbIH83D4bvXq0X7qQoSBSNP7Dvv3HYuqMhf0DaegrlBuJllFVVq9qPVRnKxt1Il2HgxOBvbhOT+9in1BzA+YJ99UzC85O0Qz06A+CmtHEy4aZ2kj5hHjECgYEAmNS4+A8Fkss8Js1RieK2LniBxMgmYml3pfVLKGnzmng7H2+cwPLhPIzIuwytXywh2bzbsYEfYx3EoEVgMEpPhoarQnYPukrJO4gwE2o5Te6T5mJSZGlQJQj9q4ZB2Dfzet6INsK0oG8XVGXSpQvQh3RUYekCZQkBBFcpqWpbIEsCgYAnM3DQf3FJoSnXaMhrVBIovic5l0xFkEHskAjFTevO86Fsz1C2aSeRKSqGFoOQ0tmJzBEs1R6KqnHInicDTQrKhArgLXX4v3CddjfTRJkFWDbE/CkvKZNOrcf1nhaGCPspRJj2KUkj1Fhl9Cncdn/RsYEONbwQSjIfMPkvxF+8HQ=="
|
|
||||||
public-key: "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu1SU1LfVLPHCozMxH2Mo4lgOEePzNm0tRgeLezV6ffAt0gunVTLw7onLRnrq0/IzW7yWR7QkrmBL7jTKEn5u+qKhbwKfBstIs+bMY2Zkp18gnTxKLxoS2tFczGkPLPgizskuemMghRniWaoLcyehkd3qqGElvW/VDL5AaWTg0nLVkjRo9z+40RQzuVaE8AkAFmxZzow3x+VJYKdjykkJ0iT9wCS0DRTXu269V264Vf/3jvredZiKRkgwlL9xNAwxXFg0x/XFw005UWVRIkdgcKWTjpBP2dPwVZ4WWC+9aGVd+Gyn1o0CLelf4rEjGoXbAAEgAqeGUxrcIlbjXfbcmwIDAQAB"
|
|
||||||
storage:
|
|
||||||
type: local
|
|
||||||
private: false
|
|
||||||
|
|
||||||
spring:
|
|
||||||
flyway:
|
|
||||||
enabled: true
|
|
||||||
clean-disabled: false
|
|
||||||
datasource:
|
|
||||||
driver-class-name: org.h2.Driver
|
|
||||||
url: "jdbc:h2:mem:test;MODE=POSTGRESQL;DB_CLOSE_DELAY=-1;CASE_INSENSITIVE_IDENTIFIERS=true;TRACE_LEVEL_FILE=4;"
|
|
||||||
username: ""
|
|
||||||
password:
|
|
||||||
data:
|
|
||||||
mongodb:
|
|
||||||
auto-index-creation: true
|
|
||||||
host: localhost
|
|
||||||
port: 27017
|
|
||||||
database: hideout-integration-test
|
|
||||||
h2:
|
|
||||||
console:
|
|
||||||
enabled: true
|
|
||||||
|
|
||||||
# exposed:
|
|
||||||
# generate-ddl: true
|
|
||||||
# excluded-packages: dev.usbharu.hideout.core.infrastructure.kjobexposed
|
|
||||||
server:
|
|
||||||
port: 8080
|
|
|
@ -1,2 +0,0 @@
|
||||||
junit.jupiter.testclass.order.default=org.junit.jupiter.api.ClassOrderer$Random
|
|
||||||
junit.jupiter.testmethod.order.default=org.junit.jupiter.api.MethodOrderer$Random
|
|
|
@ -1,11 +0,0 @@
|
||||||
<configuration>
|
|
||||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
|
||||||
<encoder>
|
|
||||||
<pattern>%d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level [%X{x-request-id}] %logger{36} - %msg%n</pattern>
|
|
||||||
</encoder>
|
|
||||||
</appender>
|
|
||||||
<root level="TRACE">
|
|
||||||
<appender-ref ref="STDOUT"/>
|
|
||||||
</root>
|
|
||||||
<logger name="org.springframework.security" level="TRACE"/>
|
|
||||||
</configuration>
|
|
Binary file not shown.
Before Width: | Height: | Size: 7.1 KiB |
|
@ -1,17 +0,0 @@
|
||||||
insert into "actors" (id, name, domain, screen_name, description, inbox, outbox, url, public_key, private_key,
|
|
||||||
created_at, key_id, following, followers, instance, locked, following_count, followers_count,
|
|
||||||
posts_count, last_post_at)
|
|
||||||
VALUES (3733363, 'follow-test-user-1', 'example.com', 'follow-test-user-1-name', '',
|
|
||||||
'https://example.com/users/follow-test-user-1/inbox',
|
|
||||||
'https://example.com/users/follow-test-user-1/outbox', 'https://example.com/users/follow-test-user-1',
|
|
||||||
'-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----',
|
|
||||||
'-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----', 12345678,
|
|
||||||
'https://example.com/users/follow-test-user-1#pubkey', 'https://example.com/users/follow-test-user-1/following',
|
|
||||||
'https://example.com/users/follow-test-user-1/followers', 0, false, 0, 0, 0, null),
|
|
||||||
(37335363, 'follow-test-user-2', 'example.com', 'follow-test-user-2-name', '',
|
|
||||||
'https://example.com/users/follow-test-user-2/inbox',
|
|
||||||
'https://example.com/users/follow-test-user-2/outbox', 'https://example.com/users/follow-test-user-2',
|
|
||||||
'-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----',
|
|
||||||
'-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----', 12345678,
|
|
||||||
'https://example.com/users/follow-test-user-2#pubkey', 'https://example.com/users/follow-test-user-2/following',
|
|
||||||
'https://example.com/users/follow-test-user-2/followers', 0, false, 0, 0, 0, null);
|
|
|
@ -1,202 +0,0 @@
|
||||||
insert into posts (id, actor_id, overview, content, text, created_at, visibility, url, repost_id, reply_id, sensitive,
|
|
||||||
ap_id, deleted)
|
|
||||||
VALUES (1, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/1',
|
|
||||||
null, null, false, 'https://example.com/users/1/posts/1', false),
|
|
||||||
(2, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/2',
|
|
||||||
null, 1, false, 'https://example.com/users/1/posts/2', false),
|
|
||||||
(3, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/3',
|
|
||||||
null, null, false, 'https://example.com/users/1/posts/3', false),
|
|
||||||
(4, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/4',
|
|
||||||
null, 3, false, 'https://example.com/users/1/posts/4', false),
|
|
||||||
(5, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/5',
|
|
||||||
null, null, false, 'https://example.com/users/1/posts/5', false),
|
|
||||||
(6, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/6',
|
|
||||||
null, null, false, 'https://example.com/users/1/posts/6', false),
|
|
||||||
(7, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/7',
|
|
||||||
null, null, false, 'https://example.com/users/1/posts/7', false),
|
|
||||||
(8, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/8',
|
|
||||||
null, 7, false, 'https://example.com/users/1/posts/8', false),
|
|
||||||
(9, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/9',
|
|
||||||
null, null, false, 'https://example.com/users/1/posts/9', false),
|
|
||||||
(10, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/10',
|
|
||||||
null, 9, false, 'https://example.com/users/1/posts/10', false),
|
|
||||||
(11, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/11',
|
|
||||||
null, null, false, 'https://example.com/users/1/posts/11', false),
|
|
||||||
(12, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/12',
|
|
||||||
null, null, false, 'https://example.com/users/1/posts/12', false),
|
|
||||||
(13, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/13',
|
|
||||||
null, null, false, 'https://example.com/users/1/posts/13', false),
|
|
||||||
(14, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/14',
|
|
||||||
null, 13, false, 'https://example.com/users/1/posts/14', false),
|
|
||||||
(15, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/15',
|
|
||||||
null, null, false, 'https://example.com/users/1/posts/15', false),
|
|
||||||
(16, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/16',
|
|
||||||
null, 15, false, 'https://example.com/users/1/posts/16', false),
|
|
||||||
(17, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/17',
|
|
||||||
null, null, false, 'https://example.com/users/1/posts/17', false),
|
|
||||||
(18, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/18',
|
|
||||||
null, null, false, 'https://example.com/users/1/posts/18', false),
|
|
||||||
(19, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/19',
|
|
||||||
null, null, false, 'https://example.com/users/1/posts/19', false),
|
|
||||||
(20, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/20',
|
|
||||||
null, 19, false, 'https://example.com/users/1/posts/20', false),
|
|
||||||
(21, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/21',
|
|
||||||
null, null, false, 'https://example.com/users/1/posts/21', false),
|
|
||||||
(22, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/22',
|
|
||||||
null, 21, false, 'https://example.com/users/1/posts/22', false),
|
|
||||||
(23, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/23',
|
|
||||||
null, null, false, 'https://example.com/users/1/posts/23', false),
|
|
||||||
(24, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/24',
|
|
||||||
null, null, false, 'https://example.com/users/1/posts/24', false),
|
|
||||||
(25, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/25',
|
|
||||||
null, null, false, 'https://example.com/users/1/posts/25', false),
|
|
||||||
(26, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/26',
|
|
||||||
null, 25, false, 'https://example.com/users/1/posts/26', false),
|
|
||||||
(27, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/27',
|
|
||||||
null, null, false, 'https://example.com/users/1/posts/27', false),
|
|
||||||
(28, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/28',
|
|
||||||
null, 27, false, 'https://example.com/users/1/posts/28', false),
|
|
||||||
(29, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/29',
|
|
||||||
null, null, false, 'https://example.com/users/1/posts/29', false),
|
|
||||||
(30, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/30',
|
|
||||||
null, null, false, 'https://example.com/users/1/posts/30', false),
|
|
||||||
(31, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/31',
|
|
||||||
null, null, false, 'https://example.com/users/1/posts/31', false),
|
|
||||||
(32, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/32',
|
|
||||||
null, 31, false, 'https://example.com/users/1/posts/32', false),
|
|
||||||
(33, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/33',
|
|
||||||
null, null, false, 'https://example.com/users/1/posts/33', false),
|
|
||||||
(34, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/34',
|
|
||||||
null, 33, false, 'https://example.com/users/1/posts/34', false),
|
|
||||||
(35, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/35',
|
|
||||||
null, null, false, 'https://example.com/users/1/posts/35', false),
|
|
||||||
(36, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/36',
|
|
||||||
null, null, false, 'https://example.com/users/1/posts/36', false),
|
|
||||||
(37, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/37',
|
|
||||||
null, null, false, 'https://example.com/users/1/posts/37', false),
|
|
||||||
(38, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/38',
|
|
||||||
null, 37, false, 'https://example.com/users/1/posts/38', false),
|
|
||||||
(39, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/39',
|
|
||||||
null, null, false, 'https://example.com/users/1/posts/39', false),
|
|
||||||
(40, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/40',
|
|
||||||
null, 39, false, 'https://example.com/users/1/posts/40', false),
|
|
||||||
(41, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/41',
|
|
||||||
null, null, false, 'https://example.com/users/1/posts/41', false),
|
|
||||||
(42, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/42',
|
|
||||||
null, null, false, 'https://example.com/users/1/posts/42', false),
|
|
||||||
(43, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/43',
|
|
||||||
null, null, false, 'https://example.com/users/1/posts/43', false),
|
|
||||||
(44, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/44',
|
|
||||||
null, 43, false, 'https://example.com/users/1/posts/44', false),
|
|
||||||
(45, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/45',
|
|
||||||
null, null, false, 'https://example.com/users/1/posts/45', false),
|
|
||||||
(46, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/46',
|
|
||||||
null, 45, false, 'https://example.com/users/1/posts/46', false),
|
|
||||||
(47, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/47',
|
|
||||||
null, null, false, 'https://example.com/users/1/posts/47', false),
|
|
||||||
(48, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/48',
|
|
||||||
null, null, false, 'https://example.com/users/1/posts/48', false),
|
|
||||||
(49, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/49',
|
|
||||||
null, null, false, 'https://example.com/users/1/posts/49', false),
|
|
||||||
(50, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/50',
|
|
||||||
null, 49, false, 'https://example.com/users/1/posts/50', false),
|
|
||||||
(51, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/51',
|
|
||||||
null, null, false, 'https://example.com/users/1/posts/51', false),
|
|
||||||
(52, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/52',
|
|
||||||
null, 51, false, 'https://example.com/users/1/posts/52', false),
|
|
||||||
(53, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/53',
|
|
||||||
null, null, false, 'https://example.com/users/1/posts/53', false),
|
|
||||||
(54, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/54',
|
|
||||||
null, null, false, 'https://example.com/users/1/posts/54', false),
|
|
||||||
(55, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/55',
|
|
||||||
null, null, false, 'https://example.com/users/1/posts/55', false),
|
|
||||||
(56, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/56',
|
|
||||||
null, 55, false, 'https://example.com/users/1/posts/56', false),
|
|
||||||
(57, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/57',
|
|
||||||
null, null, false, 'https://example.com/users/1/posts/57', false),
|
|
||||||
(58, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/58',
|
|
||||||
null, 57, false, 'https://example.com/users/1/posts/58', false),
|
|
||||||
(59, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/59',
|
|
||||||
null, null, false, 'https://example.com/users/1/posts/59', false),
|
|
||||||
(60, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/60',
|
|
||||||
null, null, false, 'https://example.com/users/1/posts/60', false),
|
|
||||||
(61, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/61',
|
|
||||||
null, null, false, 'https://example.com/users/1/posts/61', false),
|
|
||||||
(62, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/62',
|
|
||||||
null, 61, false, 'https://example.com/users/1/posts/62', false),
|
|
||||||
(63, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/63',
|
|
||||||
null, null, false, 'https://example.com/users/1/posts/63', false),
|
|
||||||
(64, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/64',
|
|
||||||
null, 63, false, 'https://example.com/users/1/posts/64', false),
|
|
||||||
(65, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/65',
|
|
||||||
null, null, false, 'https://example.com/users/1/posts/65', false),
|
|
||||||
(66, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/66',
|
|
||||||
null, null, false, 'https://example.com/users/1/posts/66', false),
|
|
||||||
(67, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/67',
|
|
||||||
null, null, false, 'https://example.com/users/1/posts/67', false),
|
|
||||||
(68, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/68',
|
|
||||||
null, 67, false, 'https://example.com/users/1/posts/68', false),
|
|
||||||
(69, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/69',
|
|
||||||
null, null, false, 'https://example.com/users/1/posts/69', false),
|
|
||||||
(70, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/70',
|
|
||||||
null, 69, false, 'https://example.com/users/1/posts/70', false),
|
|
||||||
(71, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/71',
|
|
||||||
null, null, false, 'https://example.com/users/1/posts/71', false),
|
|
||||||
(72, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/72',
|
|
||||||
null, null, false, 'https://example.com/users/1/posts/72', false),
|
|
||||||
(73, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/73',
|
|
||||||
null, null, false, 'https://example.com/users/1/posts/73', false),
|
|
||||||
(74, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/74',
|
|
||||||
null, 73, false, 'https://example.com/users/1/posts/74', false),
|
|
||||||
(75, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/75',
|
|
||||||
null, null, false, 'https://example.com/users/1/posts/75', false),
|
|
||||||
(76, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/76',
|
|
||||||
null, 75, false, 'https://example.com/users/1/posts/76', false),
|
|
||||||
(77, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/77',
|
|
||||||
null, null, false, 'https://example.com/users/1/posts/77', false),
|
|
||||||
(78, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/78',
|
|
||||||
null, null, false, 'https://example.com/users/1/posts/78', false),
|
|
||||||
(79, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/79',
|
|
||||||
null, null, false, 'https://example.com/users/1/posts/79', false),
|
|
||||||
(80, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/80',
|
|
||||||
null, 79, false, 'https://example.com/users/1/posts/80', false),
|
|
||||||
(81, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/81',
|
|
||||||
null, null, false, 'https://example.com/users/1/posts/81', false),
|
|
||||||
(82, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/82',
|
|
||||||
null, 81, false, 'https://example.com/users/1/posts/82', false),
|
|
||||||
(83, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/83',
|
|
||||||
null, null, false, 'https://example.com/users/1/posts/83', false),
|
|
||||||
(84, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/84',
|
|
||||||
null, null, false, 'https://example.com/users/1/posts/84', false),
|
|
||||||
(85, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/85',
|
|
||||||
null, null, false, 'https://example.com/users/1/posts/85', false),
|
|
||||||
(86, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/86',
|
|
||||||
null, 85, false, 'https://example.com/users/1/posts/86', false),
|
|
||||||
(87, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/87',
|
|
||||||
null, null, false, 'https://example.com/users/1/posts/87', false),
|
|
||||||
(88, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/88',
|
|
||||||
null, 87, false, 'https://example.com/users/1/posts/88', false),
|
|
||||||
(89, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/89',
|
|
||||||
null, null, false, 'https://example.com/users/1/posts/89', false),
|
|
||||||
(90, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/90',
|
|
||||||
null, null, false, 'https://example.com/users/1/posts/90', false),
|
|
||||||
(91, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/91',
|
|
||||||
null, null, false, 'https://example.com/users/1/posts/91', false),
|
|
||||||
(92, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/92',
|
|
||||||
null, 91, false, 'https://example.com/users/1/posts/92', false),
|
|
||||||
(93, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/93',
|
|
||||||
null, null, false, 'https://example.com/users/1/posts/93', false),
|
|
||||||
(94, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/94',
|
|
||||||
null, 93, false, 'https://example.com/users/1/posts/94', false),
|
|
||||||
(95, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/95',
|
|
||||||
null, null, false, 'https://example.com/users/1/posts/95', false),
|
|
||||||
(96, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/96',
|
|
||||||
null, null, false, 'https://example.com/users/1/posts/96', false),
|
|
||||||
(97, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/97',
|
|
||||||
null, null, false, 'https://example.com/users/1/posts/97', false),
|
|
||||||
(98, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/98',
|
|
||||||
null, 97, false, 'https://example.com/users/1/posts/98', false),
|
|
||||||
(99, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/99',
|
|
||||||
null, null, false, 'https://example.com/users/1/posts/99', false),
|
|
||||||
(100, 1, null, '<p>this is test</p>', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/100',
|
|
||||||
null, 99, false, 'https://example.com/users/1/posts/100', false);
|
|
|
@ -1,4 +0,0 @@
|
||||||
insert into filters (id, user_id, name, context, action)
|
|
||||||
VALUES (1, 1, 'test filter', 'home', 'warn');
|
|
||||||
insert into filter_keywords(id, filter_id, keyword, mode)
|
|
||||||
VALUES (1, 1, 'hoge', 'NONE')
|
|
|
@ -1,28 +0,0 @@
|
||||||
insert into "actors" (id, name, domain, screen_name, description, inbox, outbox, url, public_key, private_key,
|
|
||||||
created_at, key_id, following, followers, instance, locked, following_count, followers_count,
|
|
||||||
posts_count, last_post_at)
|
|
||||||
VALUES (8, 'test-user8', 'example.com', 'Im test-user8.', 'THis account is test-user8.',
|
|
||||||
'https://example.com/users/test-user8/inbox',
|
|
||||||
'https://example.com/users/test-user8/outbox', 'https://example.com/users/test-user8',
|
|
||||||
'-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----',
|
|
||||||
'-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----', 12345678,
|
|
||||||
'https://example.com/users/test-user8#pubkey', 'https://example.com/users/test-user8/following',
|
|
||||||
'https://example.com/users/test-user8/followers', 0, false, 0, 0, 0, null),
|
|
||||||
(9, 'test-user9', 'follower.example.com', 'Im test-user9.', 'THis account is test-user9.',
|
|
||||||
'https://follower.example.com/users/test-user9/inbox',
|
|
||||||
'https://follower.example.com/users/test-user9/outbox', 'https://follower.example.com/users/test-user9',
|
|
||||||
'-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----',
|
|
||||||
null, 12345678,
|
|
||||||
'https://follower.example.com/users/test-user9#pubkey',
|
|
||||||
'https://follower.example.com/users/test-user9/following',
|
|
||||||
'https://follower.example.com/users/test-user9/followers', 0, false, 0, 0, 0, null);
|
|
||||||
|
|
||||||
insert into relationships (actor_id, target_actor_id, following, blocking, muting, follow_request,
|
|
||||||
ignore_follow_request)
|
|
||||||
VALUES (9, 8, true, false, false, false, false);
|
|
||||||
|
|
||||||
insert into POSTS (ID, ACTOR_ID, OVERVIEW, CONTENT, TEXT, CREATED_AT, VISIBILITY, URL, REPLY_ID, REPOST_ID, SENSITIVE,
|
|
||||||
AP_ID)
|
|
||||||
VALUES (1239, 8, null, '<p>test post</p>', 'test post', 12345680, 2, 'https://example.com/users/test-user8/posts/1239',
|
|
||||||
null, null, false,
|
|
||||||
'https://example.com/users/test-user8/posts/1239');
|
|
|
@ -1,29 +0,0 @@
|
||||||
insert into "actors" (id, name, domain, screen_name, description, inbox, outbox, url, public_key, private_key,
|
|
||||||
created_at, key_id, following, followers, instance, locked, following_count, followers_count,
|
|
||||||
posts_count, last_post_at)
|
|
||||||
VALUES (4, 'test-user4', 'example.com', 'Im test user4.', 'THis account is test user4.',
|
|
||||||
'https://example.com/users/test-user4/inbox',
|
|
||||||
'https://example.com/users/test-user4/outbox', 'https://example.com/users/test-user4',
|
|
||||||
'-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----',
|
|
||||||
'-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----', 12345678,
|
|
||||||
'https://example.com/users/test-user4#pubkey', 'https://example.com/users/test-user4/following',
|
|
||||||
'https://example.com/users/test-user4/followers', 0, false, 0, 0, 0, null),
|
|
||||||
(5, 'test-user5', 'follower.example.com', 'Im test user5.', 'THis account is test user5.',
|
|
||||||
'https://follower.example.com/users/test-user5/inbox',
|
|
||||||
'https://follower.example.com/users/test-user5/outbox', 'https://follower.example.com/users/test-user5',
|
|
||||||
'-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----',
|
|
||||||
null, 12345678,
|
|
||||||
'https://follower.example.com/users/test-user5#pubkey',
|
|
||||||
'https://follower.example.com/users/test-user5/following',
|
|
||||||
'https://follower.example.com/users/test-user5/followers', 0, false, 0, 0, 0, null);
|
|
||||||
|
|
||||||
insert into relationships (actor_id, target_actor_id, following, blocking, muting, follow_request,
|
|
||||||
ignore_follow_request)
|
|
||||||
VALUES (5, 4, true, false, false, false, false);
|
|
||||||
|
|
||||||
insert into POSTS (ID, "actor_id", OVERVIEW, CONTENT, TEXT, "created_at", VISIBILITY, URL, "repost_id", "reply_id",
|
|
||||||
SENSITIVE,
|
|
||||||
AP_ID)
|
|
||||||
VALUES (1237, 4, null, '<p>test post</p>', 'test post', 12345680, 0, 'https://example.com/users/test-user4/posts/1237',
|
|
||||||
null, null, false,
|
|
||||||
'https://example.com/users/test-user4/posts/1237');
|
|
|
@ -1,29 +0,0 @@
|
||||||
insert into "actors" (id, name, domain, screen_name, description, inbox, outbox, url, public_key, private_key,
|
|
||||||
created_at, key_id, following, followers, instance, locked, following_count, followers_count,
|
|
||||||
posts_count, last_post_at)
|
|
||||||
VALUES (6, 'test-user6', 'example.com', 'Im test-user6.', 'THis account is test-user6.',
|
|
||||||
'https://example.com/users/test-user6/inbox',
|
|
||||||
'https://example.com/users/test-user6/outbox', 'https://example.com/users/test-user6',
|
|
||||||
'-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----',
|
|
||||||
'-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----', 12345678,
|
|
||||||
'https://example.com/users/test-user6#pubkey', 'https://example.com/users/test-user6/following',
|
|
||||||
'https://example.com/users/test-user6/followers', 0, false, 0, 0, 0, null),
|
|
||||||
(7, 'test-user7', 'follower.example.com', 'Im test-user7.', 'THis account is test-user7.',
|
|
||||||
'https://follower.example.com/users/test-user7/inbox',
|
|
||||||
'https://follower.example.com/users/test-user7/outbox', 'https://follower.example.com/users/test-user7',
|
|
||||||
'-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----',
|
|
||||||
null, 12345678,
|
|
||||||
'https://follower.example.com/users/test-user7#pubkey',
|
|
||||||
'https://follower.example.com/users/test-user7/following',
|
|
||||||
'https://follower.example.com/users/test-user7/followers', 0, false, 0, 0, 0, null);
|
|
||||||
|
|
||||||
insert into relationships (actor_id, target_actor_id, following, blocking, muting, follow_request,
|
|
||||||
ignore_follow_request)
|
|
||||||
VALUES (7, 6, true, false, false, false, false);
|
|
||||||
|
|
||||||
insert into POSTS (ID, "actor_ID", OVERVIEW, CONTENT, TEXT, "CREATED_AT", VISIBILITY, URL, "REPOST_ID", "REPLY_ID",
|
|
||||||
SENSITIVE,
|
|
||||||
AP_ID)
|
|
||||||
VALUES (1238, 6, null, '<p>test post</p>', 'test post', 12345680, 1, 'https://example.com/users/test-user6/posts/1238',
|
|
||||||
null, null, false,
|
|
||||||
'https://example.com/users/test-user6/posts/1238');
|
|
|
@ -1,25 +0,0 @@
|
||||||
insert into "actors" (id, name, domain, screen_name, description, inbox, outbox, url, public_key, private_key,
|
|
||||||
created_at, key_id, following, followers, instance, locked, following_count, followers_count,
|
|
||||||
posts_count, last_post_at)
|
|
||||||
VALUES (11, 'test-user11', 'example.com', 'Im test-user11.', 'THis account is test-user11.',
|
|
||||||
'https://example.com/users/test-user11/inbox',
|
|
||||||
'https://example.com/users/test-user11/outbox', 'https://example.com/users/test-user11',
|
|
||||||
'-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----',
|
|
||||||
'-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----', 12345678,
|
|
||||||
'https://example.com/users/test-user11#pubkey', 'https://example.com/users/test-user11/following',
|
|
||||||
'https://example.com/users/test-user11/followers', 0, false, 0, 0, 0, null);
|
|
||||||
|
|
||||||
insert into POSTS (id, actor_id, overview, content, text, created_at, visibility, url, repost_id, reply_id, sensitive,
|
|
||||||
ap_id,
|
|
||||||
deleted)
|
|
||||||
VALUES (1242, 11, null, '<p>test post</p>', 'test post', 12345680, 0,
|
|
||||||
'https://example.com/users/test-user11/posts/1242', null, null, false,
|
|
||||||
'https://example.com/users/test-user11/posts/1242', false);
|
|
||||||
|
|
||||||
insert into MEDIA (ID, NAME, URL, REMOTE_URL, THUMBNAIL_URL, TYPE, BLURHASH, MIME_TYPE, DESCRIPTION)
|
|
||||||
VALUES (1, 'test-media', 'https://example.com/media/test-media.png', null, null, 0, null, 'image/png', null),
|
|
||||||
(2, 'test-media2', 'https://example.com/media/test-media2.png', null, null, 0, null, 'image/png', null);
|
|
||||||
|
|
||||||
insert into POSTS_MEDIA(POST_ID, MEDIA_ID)
|
|
||||||
VALUES (1242, 1),
|
|
||||||
(1242, 2);
|
|
|
@ -1,20 +0,0 @@
|
||||||
insert into "actors" (id, name, domain, screen_name, description, inbox, outbox, url, public_key, private_key,
|
|
||||||
created_at, key_id, following, followers, instance, locked, following_count, followers_count,
|
|
||||||
posts_count, last_post_at)
|
|
||||||
VALUES (10, 'test-user10', 'example.com', 'Im test-user10.', 'THis account is test-user10.',
|
|
||||||
'https://example.com/users/test-user10/inbox',
|
|
||||||
'https://example.com/users/test-user10/outbox', 'https://example.com/users/test-user10',
|
|
||||||
'-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----',
|
|
||||||
'-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----', 12345678,
|
|
||||||
'https://example.com/users/test-user10#pubkey', 'https://example.com/users/test-user10/following',
|
|
||||||
'https://example.com/users/test-user10/followers', 0, false, 0, 0, 0, null);
|
|
||||||
|
|
||||||
insert into POSTS (id, actor_id, overview, content, text, created_at, visibility, url, repost_id, reply_id, sensitive,
|
|
||||||
ap_id,
|
|
||||||
deleted)
|
|
||||||
VALUES (1240, 10, null, '<p>test post</p>', 'test post', 12345680, 0,
|
|
||||||
'https://example.com/users/test-user10/posts/1240', null, null, false,
|
|
||||||
'https://example.com/users/test-user10/posts/1240', false),
|
|
||||||
(1241, 10, null, '<p>test post</p>', 'test post', 12345680, 0,
|
|
||||||
'https://example.com/users/test-user10/posts/1241', null, 1240, false,
|
|
||||||
'https://example.com/users/test-user10/posts/1241', false);
|
|
|
@ -1,17 +0,0 @@
|
||||||
insert into "actors" (id, name, domain, screen_name, description, inbox, outbox, url, public_key, private_key,
|
|
||||||
created_at, key_id, following, followers, instance, locked, following_count, followers_count,
|
|
||||||
posts_count, last_post_at)
|
|
||||||
VALUES (3, 'test-user3', 'example.com', 'Im test user3.', 'THis account is test user3.',
|
|
||||||
'https://example.com/users/test-user3/inbox',
|
|
||||||
'https://example.com/users/test-user3/outbox', 'https://example.com/users/test-user3',
|
|
||||||
'-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----',
|
|
||||||
'-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----', 12345678,
|
|
||||||
'https://example.com/users/test-user3#pubkey', 'https://example.com/users/test-user3/following',
|
|
||||||
'https://example.com/users/test-user3/followers', 0, false, 0, 0, 0, null);
|
|
||||||
|
|
||||||
insert into POSTS (id, actor_id, overview, content, text, created_at, visibility, url, repost_id, reply_id, sensitive,
|
|
||||||
ap_id,
|
|
||||||
deleted)
|
|
||||||
VALUES (1236, 3, null, '<p>test post</p>', 'test post', 12345680, 2, 'https://example.com/users/test-user3/posts/1236',
|
|
||||||
null, null, false,
|
|
||||||
'https://example.com/users/test-user3/posts/1236', false)
|
|
|
@ -1,17 +0,0 @@
|
||||||
insert into actors (id, name, domain, screen_name, description, inbox, outbox, url, public_key, private_key, created_at,
|
|
||||||
key_id, following, followers, instance, locked, following_count, followers_count, posts_count,
|
|
||||||
last_post_at)
|
|
||||||
VALUES (1, 'test-user', 'example.com', 'Im test user.', 'THis account is test user.',
|
|
||||||
'https://example.com/users/test-user/inbox',
|
|
||||||
'https://example.com/users/test-user/outbox', 'https://example.com/users/test-user',
|
|
||||||
'-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----',
|
|
||||||
'-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----', 12345678,
|
|
||||||
'https://example.com/users/test-user#pubkey', 'https://example.com/users/test-user/following',
|
|
||||||
'https://example.com/users/test-users/followers', 0, false, 0, 0, 0, null);
|
|
||||||
|
|
||||||
insert into POSTS (id, actor_id, overview, content, text, created_at, visibility, url, repost_id, reply_id, sensitive,
|
|
||||||
ap_id,
|
|
||||||
deleted)
|
|
||||||
VALUES (1234, 1, null, '<p>test post</p>', 'test post', 12345680, 0, 'https://example.com/users/test-user/posts/1234',
|
|
||||||
null, null, false,
|
|
||||||
'https://example.com/users/test-user/posts/1234', false)
|
|
|
@ -1,17 +0,0 @@
|
||||||
insert into actors (id, name, domain, screen_name, description, inbox, outbox, url, public_key, private_key, created_at,
|
|
||||||
key_id, following, followers, instance, locked, following_count, followers_count, posts_count,
|
|
||||||
last_post_at)
|
|
||||||
VALUES (2, 'test-user2', 'example.com', 'Im test user2.', 'THis account is test user2.',
|
|
||||||
'https://example.com/users/test-user2/inbox',
|
|
||||||
'https://example.com/users/test-user2/outbox', 'https://example.com/users/test-user2',
|
|
||||||
'-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----',
|
|
||||||
'-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----', 12345678,
|
|
||||||
'https://example.com/users/test-user2#pubkey', 'https://example.com/users/test-user2/following',
|
|
||||||
'https://example.com/users/test-user2/followers', 0, false, 0, 0, 0, null);
|
|
||||||
|
|
||||||
insert into POSTS (id, actor_id, overview, content, text, created_at, visibility, url, repost_id, reply_id, sensitive,
|
|
||||||
ap_id,
|
|
||||||
deleted)
|
|
||||||
VALUES (1235, 2, null, '<p>test post</p>', 'test post', 12345680, 1, 'https://example.com/users/test-user2/posts/1235',
|
|
||||||
null, null, false,
|
|
||||||
'https://example.com/users/test-user2/posts/1235', false)
|
|
|
@ -1,66 +0,0 @@
|
||||||
insert into mastodon_notifications (id, user_id, type, created_at, account_id, status_id, report_id, relationship_serverance_event_id)
|
|
||||||
values (1, 1, 'follow', current_timestamp, 2, null, null, null),
|
|
||||||
(2, 1, 'follow', current_timestamp, 2, null, null, null),
|
|
||||||
(3, 1, 'follow', current_timestamp, 2, null, null, null),
|
|
||||||
(4, 1, 'follow', current_timestamp, 2, null, null, null),
|
|
||||||
(5, 1, 'follow', current_timestamp, 2, null, null, null),
|
|
||||||
(6, 1, 'follow', current_timestamp, 2, null, null, null),
|
|
||||||
(7, 1, 'follow', current_timestamp, 2, null, null, null),
|
|
||||||
(8, 1, 'follow', current_timestamp, 2, null, null, null),
|
|
||||||
(9, 1, 'follow', current_timestamp, 2, null, null, null),
|
|
||||||
(10, 1, 'follow', current_timestamp, 2, null, null, null),
|
|
||||||
(11, 1, 'follow', current_timestamp, 2, null, null, null),
|
|
||||||
(12, 1, 'follow', current_timestamp, 2, null, null, null),
|
|
||||||
(13, 1, 'follow', current_timestamp, 2, null, null, null),
|
|
||||||
(14, 1, 'follow', current_timestamp, 2, null, null, null),
|
|
||||||
(15, 1, 'follow', current_timestamp, 2, null, null, null),
|
|
||||||
(16, 1, 'follow', current_timestamp, 2, null, null, null),
|
|
||||||
(17, 1, 'follow', current_timestamp, 2, null, null, null),
|
|
||||||
(18, 1, 'follow', current_timestamp, 2, null, null, null),
|
|
||||||
(19, 1, 'follow', current_timestamp, 2, null, null, null),
|
|
||||||
(20, 1, 'follow', current_timestamp, 2, null, null, null),
|
|
||||||
(21, 1, 'follow', current_timestamp, 2, null, null, null),
|
|
||||||
(22, 1, 'follow', current_timestamp, 2, null, null, null),
|
|
||||||
(23, 1, 'follow', current_timestamp, 2, null, null, null),
|
|
||||||
(24, 1, 'follow', current_timestamp, 2, null, null, null),
|
|
||||||
(25, 1, 'follow', current_timestamp, 2, null, null, null),
|
|
||||||
(26, 1, 'follow', current_timestamp, 2, null, null, null),
|
|
||||||
(27, 1, 'follow', current_timestamp, 2, null, null, null),
|
|
||||||
(28, 1, 'follow', current_timestamp, 2, null, null, null),
|
|
||||||
(29, 1, 'follow', current_timestamp, 2, null, null, null),
|
|
||||||
(30, 1, 'follow', current_timestamp, 2, null, null, null),
|
|
||||||
(31, 1, 'follow', current_timestamp, 2, null, null, null),
|
|
||||||
(32, 1, 'follow', current_timestamp, 2, null, null, null),
|
|
||||||
(33, 1, 'follow', current_timestamp, 2, null, null, null),
|
|
||||||
(34, 1, 'follow', current_timestamp, 2, null, null, null),
|
|
||||||
(35, 1, 'follow', current_timestamp, 2, null, null, null),
|
|
||||||
(36, 1, 'follow', current_timestamp, 2, null, null, null),
|
|
||||||
(37, 1, 'follow', current_timestamp, 2, null, null, null),
|
|
||||||
(38, 1, 'follow', current_timestamp, 2, null, null, null),
|
|
||||||
(39, 1, 'follow', current_timestamp, 2, null, null, null),
|
|
||||||
(40, 1, 'follow', current_timestamp, 2, null, null, null),
|
|
||||||
(41, 1, 'follow', current_timestamp, 2, null, null, null),
|
|
||||||
(42, 1, 'follow', current_timestamp, 2, null, null, null),
|
|
||||||
(43, 1, 'follow', current_timestamp, 2, null, null, null),
|
|
||||||
(44, 1, 'follow', current_timestamp, 2, null, null, null),
|
|
||||||
(45, 1, 'follow', current_timestamp, 2, null, null, null),
|
|
||||||
(46, 1, 'follow', current_timestamp, 2, null, null, null),
|
|
||||||
(47, 1, 'follow', current_timestamp, 2, null, null, null),
|
|
||||||
(48, 1, 'follow', current_timestamp, 2, null, null, null),
|
|
||||||
(49, 1, 'follow', current_timestamp, 2, null, null, null),
|
|
||||||
(50, 1, 'follow', current_timestamp, 2, null, null, null),
|
|
||||||
(51, 1, 'follow', current_timestamp, 2, null, null, null),
|
|
||||||
(52, 1, 'follow', current_timestamp, 2, null, null, null),
|
|
||||||
(53, 1, 'follow', current_timestamp, 2, null, null, null),
|
|
||||||
(54, 1, 'follow', current_timestamp, 2, null, null, null),
|
|
||||||
(55, 1, 'follow', current_timestamp, 2, null, null, null),
|
|
||||||
(56, 1, 'follow', current_timestamp, 2, null, null, null),
|
|
||||||
(57, 1, 'follow', current_timestamp, 2, null, null, null),
|
|
||||||
(58, 1, 'follow', current_timestamp, 2, null, null, null),
|
|
||||||
(59, 1, 'follow', current_timestamp, 2, null, null, null),
|
|
||||||
(60, 1, 'follow', current_timestamp, 2, null, null, null),
|
|
||||||
(61, 1, 'follow', current_timestamp, 2, null, null, null),
|
|
||||||
(62, 1, 'follow', current_timestamp, 2, null, null, null),
|
|
||||||
(63, 1, 'follow', current_timestamp, 2, null, null, null),
|
|
||||||
(64, 1, 'follow', current_timestamp, 2, null, null, null),
|
|
||||||
(65, 1, 'follow', current_timestamp, 2, null, null, null);
|
|
|
@ -1,66 +0,0 @@
|
||||||
insert into notifications(id, type, user_id, source_actor_id, post_id, text, reaction_id, created_at)
|
|
||||||
VALUES (1, 'follow', 1, 2, null, null, null, current_timestamp),
|
|
||||||
(2, 'follow', 1, 2, null, null, null, current_timestamp),
|
|
||||||
(3, 'follow', 1, 2, null, null, null, current_timestamp),
|
|
||||||
(4, 'follow', 1, 2, null, null, null, current_timestamp),
|
|
||||||
(5, 'follow', 1, 2, null, null, null, current_timestamp),
|
|
||||||
(6, 'follow', 1, 2, null, null, null, current_timestamp),
|
|
||||||
(7, 'follow', 1, 2, null, null, null, current_timestamp),
|
|
||||||
(8, 'follow', 1, 2, null, null, null, current_timestamp),
|
|
||||||
(9, 'follow', 1, 2, null, null, null, current_timestamp),
|
|
||||||
(10, 'follow', 1, 2, null, null, null, current_timestamp),
|
|
||||||
(11, 'follow', 1, 2, null, null, null, current_timestamp),
|
|
||||||
(12, 'follow', 1, 2, null, null, null, current_timestamp),
|
|
||||||
(13, 'follow', 1, 2, null, null, null, current_timestamp),
|
|
||||||
(14, 'follow', 1, 2, null, null, null, current_timestamp),
|
|
||||||
(15, 'follow', 1, 2, null, null, null, current_timestamp),
|
|
||||||
(16, 'follow', 1, 2, null, null, null, current_timestamp),
|
|
||||||
(17, 'follow', 1, 2, null, null, null, current_timestamp),
|
|
||||||
(18, 'follow', 1, 2, null, null, null, current_timestamp),
|
|
||||||
(19, 'follow', 1, 2, null, null, null, current_timestamp),
|
|
||||||
(20, 'follow', 1, 2, null, null, null, current_timestamp),
|
|
||||||
(21, 'follow', 1, 2, null, null, null, current_timestamp),
|
|
||||||
(22, 'follow', 1, 2, null, null, null, current_timestamp),
|
|
||||||
(23, 'follow', 1, 2, null, null, null, current_timestamp),
|
|
||||||
(24, 'follow', 1, 2, null, null, null, current_timestamp),
|
|
||||||
(25, 'follow', 1, 2, null, null, null, current_timestamp),
|
|
||||||
(26, 'follow', 1, 2, null, null, null, current_timestamp),
|
|
||||||
(27, 'follow', 1, 2, null, null, null, current_timestamp),
|
|
||||||
(28, 'follow', 1, 2, null, null, null, current_timestamp),
|
|
||||||
(29, 'follow', 1, 2, null, null, null, current_timestamp),
|
|
||||||
(30, 'follow', 1, 2, null, null, null, current_timestamp),
|
|
||||||
(31, 'follow', 1, 2, null, null, null, current_timestamp),
|
|
||||||
(32, 'follow', 1, 2, null, null, null, current_timestamp),
|
|
||||||
(33, 'follow', 1, 2, null, null, null, current_timestamp),
|
|
||||||
(34, 'follow', 1, 2, null, null, null, current_timestamp),
|
|
||||||
(35, 'follow', 1, 2, null, null, null, current_timestamp),
|
|
||||||
(36, 'follow', 1, 2, null, null, null, current_timestamp),
|
|
||||||
(37, 'follow', 1, 2, null, null, null, current_timestamp),
|
|
||||||
(38, 'follow', 1, 2, null, null, null, current_timestamp),
|
|
||||||
(39, 'follow', 1, 2, null, null, null, current_timestamp),
|
|
||||||
(40, 'follow', 1, 2, null, null, null, current_timestamp),
|
|
||||||
(41, 'follow', 1, 2, null, null, null, current_timestamp),
|
|
||||||
(42, 'follow', 1, 2, null, null, null, current_timestamp),
|
|
||||||
(43, 'follow', 1, 2, null, null, null, current_timestamp),
|
|
||||||
(44, 'follow', 1, 2, null, null, null, current_timestamp),
|
|
||||||
(45, 'follow', 1, 2, null, null, null, current_timestamp),
|
|
||||||
(46, 'follow', 1, 2, null, null, null, current_timestamp),
|
|
||||||
(47, 'follow', 1, 2, null, null, null, current_timestamp),
|
|
||||||
(48, 'follow', 1, 2, null, null, null, current_timestamp),
|
|
||||||
(49, 'follow', 1, 2, null, null, null, current_timestamp),
|
|
||||||
(50, 'follow', 1, 2, null, null, null, current_timestamp),
|
|
||||||
(51, 'follow', 1, 2, null, null, null, current_timestamp),
|
|
||||||
(52, 'follow', 1, 2, null, null, null, current_timestamp),
|
|
||||||
(53, 'follow', 1, 2, null, null, null, current_timestamp),
|
|
||||||
(54, 'follow', 1, 2, null, null, null, current_timestamp),
|
|
||||||
(55, 'follow', 1, 2, null, null, null, current_timestamp),
|
|
||||||
(56, 'follow', 1, 2, null, null, null, current_timestamp),
|
|
||||||
(57, 'follow', 1, 2, null, null, null, current_timestamp),
|
|
||||||
(58, 'follow', 1, 2, null, null, null, current_timestamp),
|
|
||||||
(59, 'follow', 1, 2, null, null, null, current_timestamp),
|
|
||||||
(60, 'follow', 1, 2, null, null, null, current_timestamp),
|
|
||||||
(61, 'follow', 1, 2, null, null, null, current_timestamp),
|
|
||||||
(62, 'follow', 1, 2, null, null, null, current_timestamp),
|
|
||||||
(63, 'follow', 1, 2, null, null, null, current_timestamp),
|
|
||||||
(64, 'follow', 1, 2, null, null, null, current_timestamp),
|
|
||||||
(65, 'follow', 1, 2, null, null, null, current_timestamp);
|
|
|
@ -1,3 +0,0 @@
|
||||||
insert into emojis(id, name, domain, instance_id, url, category, created_at)
|
|
||||||
VALUES (1, 'kotlin', 'example.com', null, 'https://example.com/emojis/kotlin', null,
|
|
||||||
TIMESTAMP '2024-01-08 07:51:30.036Z');
|
|
|
@ -1,4 +0,0 @@
|
||||||
insert into posts (id, actor_id, overview, content, text, created_at, visibility, url, repost_id, reply_id, sensitive,
|
|
||||||
ap_id)
|
|
||||||
VALUES (1, 1, null, '<p>test post</p>', 'hello', 1234455, 0, 'https://localhost/users/1/posts/1', null, null, false,
|
|
||||||
'https://users/1/posts/1');
|
|
|
@ -1,10 +0,0 @@
|
||||||
insert into "actors" (id, name, domain, screen_name, description, inbox, outbox, url, public_key, private_key,
|
|
||||||
created_at, key_id, following, followers, instance, locked, following_count, followers_count,
|
|
||||||
posts_count, last_post_at)
|
|
||||||
VALUES (1, 'test-user', 'example.com', 'Im test user.', 'THis account is test user.',
|
|
||||||
'https://example.com/users/test-user/inbox',
|
|
||||||
'https://example.com/users/test-user/outbox', 'https://example.com/users/test-user',
|
|
||||||
'-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----',
|
|
||||||
'-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----', 12345678,
|
|
||||||
'https://example.com/users/test-user#pubkey', 'https://example.com/users/test-user/following',
|
|
||||||
'https://example.com/users/test-users/followers', 0, false, 0, 0, 0, null);
|
|
|
@ -1,10 +0,0 @@
|
||||||
insert into "actors" (id, name, domain, screen_name, description, inbox, outbox, url, public_key, private_key,
|
|
||||||
created_at, key_id, following, followers, instance, locked, following_count, followers_count,
|
|
||||||
posts_count, last_post_at)
|
|
||||||
VALUES (2, 'test-user2', 'example.com', 'Im test user.', 'THis account is test user.',
|
|
||||||
'https://example.com/users/test-user2/inbox',
|
|
||||||
'https://example.com/users/test-user2/outbox', 'https://example.com/users/test-user2',
|
|
||||||
'-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----',
|
|
||||||
'-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----', 12345678,
|
|
||||||
'https://example.com/users/test-user2#pubkey', 'https://example.com/users/test-user2/following',
|
|
||||||
'https://example.com/users/test-user2s/followers', 0, false, 0, 0, 0, null);
|
|
|
@ -1,41 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.activitypub.domain
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.activitypub.domain.model.StringOrObject
|
|
||||||
|
|
||||||
object Constant {
|
|
||||||
val context = listOf(
|
|
||||||
StringOrObject("https://www.w3.org/ns/activitystreams"),
|
|
||||||
StringOrObject("https://w3id.org/security/v1"),
|
|
||||||
StringOrObject(
|
|
||||||
mapOf(
|
|
||||||
"manuallyApprovesFollowers" to "as:manuallyApprovesFollowers",
|
|
||||||
"sensitive" to "as:sensitive",
|
|
||||||
"Hashtag" to "as:Hashtag",
|
|
||||||
"quoteUrl" to "as:quoteUrl",
|
|
||||||
"toot" to "http://joinmastodon.org/ns#",
|
|
||||||
"Emoji" to "toot:Emoji",
|
|
||||||
"featured" to "toot:featured",
|
|
||||||
"discoverable" to "toot:discoverable",
|
|
||||||
"schema" to "http://schema.org#",
|
|
||||||
"PropertyValue" to "schema:PropertyValue",
|
|
||||||
"value" to "schema:value",
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -1,37 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.activitypub.domain.exception
|
|
||||||
|
|
||||||
import java.io.Serial
|
|
||||||
|
|
||||||
class ActivityPubProcessException : RuntimeException {
|
|
||||||
constructor() : super()
|
|
||||||
constructor(message: String?) : super(message)
|
|
||||||
constructor(message: String?, cause: Throwable?) : super(message, cause)
|
|
||||||
constructor(cause: Throwable?) : super(cause)
|
|
||||||
constructor(message: String?, cause: Throwable?, enableSuppression: Boolean, writableStackTrace: Boolean) : super(
|
|
||||||
message,
|
|
||||||
cause,
|
|
||||||
enableSuppression,
|
|
||||||
writableStackTrace
|
|
||||||
)
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
@Serial
|
|
||||||
private const val serialVersionUID: Long = 5370068873167636639L
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,37 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.activitypub.domain.exception
|
|
||||||
|
|
||||||
import java.io.Serial
|
|
||||||
|
|
||||||
class FailedProcessException : RuntimeException {
|
|
||||||
constructor() : super()
|
|
||||||
constructor(message: String?) : super(message)
|
|
||||||
constructor(message: String?, cause: Throwable?) : super(message, cause)
|
|
||||||
constructor(cause: Throwable?) : super(cause)
|
|
||||||
constructor(message: String?, cause: Throwable?, enableSuppression: Boolean, writableStackTrace: Boolean) : super(
|
|
||||||
message,
|
|
||||||
cause,
|
|
||||||
enableSuppression,
|
|
||||||
writableStackTrace
|
|
||||||
)
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
@Serial
|
|
||||||
private const val serialVersionUID: Long = -1305337651143409144L
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.activitypub.domain.exception
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException
|
|
||||||
import java.io.Serial
|
|
||||||
|
|
||||||
class FailedToGetActivityPubResourceException : FailedToGetResourcesException {
|
|
||||||
constructor() : super()
|
|
||||||
constructor(s: String?) : super(s)
|
|
||||||
constructor(message: String?, cause: Throwable?) : super(message, cause)
|
|
||||||
constructor(cause: Throwable?) : super(cause)
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
@Serial
|
|
||||||
private const val serialVersionUID: Long = 6420233106776818052L
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,37 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.activitypub.domain.exception
|
|
||||||
|
|
||||||
import java.io.Serial
|
|
||||||
|
|
||||||
class HttpSignatureUnauthorizedException : RuntimeException {
|
|
||||||
constructor() : super()
|
|
||||||
constructor(message: String?) : super(message)
|
|
||||||
constructor(message: String?, cause: Throwable?) : super(message, cause)
|
|
||||||
constructor(cause: Throwable?) : super(cause)
|
|
||||||
constructor(message: String?, cause: Throwable?, enableSuppression: Boolean, writableStackTrace: Boolean) : super(
|
|
||||||
message,
|
|
||||||
cause,
|
|
||||||
enableSuppression,
|
|
||||||
writableStackTrace
|
|
||||||
)
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
@Serial
|
|
||||||
private const val serialVersionUID: Long = -6449793151674654501L
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,62 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.activitypub.domain.model
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonCreator
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty
|
|
||||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize
|
|
||||||
import dev.usbharu.hideout.activitypub.domain.model.objects.Object
|
|
||||||
import dev.usbharu.hideout.activitypub.domain.model.objects.ObjectDeserializer
|
|
||||||
|
|
||||||
open class Accept @JsonCreator constructor(
|
|
||||||
type: List<String> = emptyList(),
|
|
||||||
@JsonDeserialize(using = ObjectDeserializer::class)
|
|
||||||
@JsonProperty("object")
|
|
||||||
val apObject: Object,
|
|
||||||
override val actor: String
|
|
||||||
) : Object(
|
|
||||||
type = add(type, "Accept")
|
|
||||||
),
|
|
||||||
HasActor {
|
|
||||||
override fun equals(other: Any?): Boolean {
|
|
||||||
if (this === other) return true
|
|
||||||
if (javaClass != other?.javaClass) return false
|
|
||||||
if (!super.equals(other)) return false
|
|
||||||
|
|
||||||
other as Accept
|
|
||||||
|
|
||||||
if (apObject != other.apObject) return false
|
|
||||||
if (actor != other.actor) return false
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
|
||||||
var result = super.hashCode()
|
|
||||||
result = 31 * result + apObject.hashCode()
|
|
||||||
result = 31 * result + actor.hashCode()
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toString(): String {
|
|
||||||
return "Accept(" +
|
|
||||||
"apObject=$apObject, " +
|
|
||||||
"actor='$actor'" +
|
|
||||||
")" +
|
|
||||||
" ${super.toString()}"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,76 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.activitypub.domain.model
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonCreator
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty
|
|
||||||
import dev.usbharu.hideout.activitypub.domain.model.objects.Object
|
|
||||||
|
|
||||||
open class Announce @JsonCreator constructor(
|
|
||||||
type: List<String> = emptyList(),
|
|
||||||
@JsonProperty("object")
|
|
||||||
val apObject: String,
|
|
||||||
override val actor: String,
|
|
||||||
override val id: String,
|
|
||||||
val published: String,
|
|
||||||
val to: List<String> = emptyList(),
|
|
||||||
val cc: List<String> = emptyList()
|
|
||||||
) : Object(
|
|
||||||
type = add(type, "Announce")
|
|
||||||
),
|
|
||||||
HasActor,
|
|
||||||
HasId {
|
|
||||||
override fun equals(other: Any?): Boolean {
|
|
||||||
if (this === other) return true
|
|
||||||
if (javaClass != other?.javaClass) return false
|
|
||||||
if (!super.equals(other)) return false
|
|
||||||
|
|
||||||
other as Announce
|
|
||||||
|
|
||||||
if (apObject != other.apObject) return false
|
|
||||||
if (actor != other.actor) return false
|
|
||||||
if (id != other.id) return false
|
|
||||||
if (published != other.published) return false
|
|
||||||
if (to != other.to) return false
|
|
||||||
if (cc != other.cc) return false
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
|
||||||
var result = super.hashCode()
|
|
||||||
result = 31 * result + apObject.hashCode()
|
|
||||||
result = 31 * result + actor.hashCode()
|
|
||||||
result = 31 * result + id.hashCode()
|
|
||||||
result = 31 * result + published.hashCode()
|
|
||||||
result = 31 * result + to.hashCode()
|
|
||||||
result = 31 * result + cc.hashCode()
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toString(): String {
|
|
||||||
return "Announce(" +
|
|
||||||
"apObject='$apObject', " +
|
|
||||||
"actor='$actor', " +
|
|
||||||
"id='$id', " +
|
|
||||||
"published='$published', " +
|
|
||||||
"to=$to, " +
|
|
||||||
"cc=$cc" +
|
|
||||||
")" +
|
|
||||||
" ${super.toString()}"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,79 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.activitypub.domain.model
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty
|
|
||||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize
|
|
||||||
import dev.usbharu.hideout.activitypub.domain.model.objects.Object
|
|
||||||
import dev.usbharu.hideout.activitypub.domain.model.objects.ObjectDeserializer
|
|
||||||
|
|
||||||
open class Create(
|
|
||||||
type: List<String> = emptyList(),
|
|
||||||
val name: String? = null,
|
|
||||||
@JsonDeserialize(using = ObjectDeserializer::class)
|
|
||||||
@JsonProperty("object")
|
|
||||||
val apObject: Object,
|
|
||||||
override val actor: String,
|
|
||||||
override val id: String,
|
|
||||||
val to: List<String> = emptyList(),
|
|
||||||
val cc: List<String> = emptyList()
|
|
||||||
) : Object(
|
|
||||||
type = add(type, "Create")
|
|
||||||
),
|
|
||||||
HasId,
|
|
||||||
HasActor {
|
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
|
||||||
if (this === other) return true
|
|
||||||
if (javaClass != other?.javaClass) return false
|
|
||||||
if (!super.equals(other)) return false
|
|
||||||
|
|
||||||
other as Create
|
|
||||||
|
|
||||||
if (name != other.name) return false
|
|
||||||
if (apObject != other.apObject) return false
|
|
||||||
if (actor != other.actor) return false
|
|
||||||
if (id != other.id) return false
|
|
||||||
if (to != other.to) return false
|
|
||||||
if (cc != other.cc) return false
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
|
||||||
var result = super.hashCode()
|
|
||||||
result = 31 * result + (name?.hashCode() ?: 0)
|
|
||||||
result = 31 * result + apObject.hashCode()
|
|
||||||
result = 31 * result + actor.hashCode()
|
|
||||||
result = 31 * result + id.hashCode()
|
|
||||||
result = 31 * result + to.hashCode()
|
|
||||||
result = 31 * result + cc.hashCode()
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toString(): String {
|
|
||||||
return "Create(" +
|
|
||||||
"name=$name, " +
|
|
||||||
"apObject=$apObject, " +
|
|
||||||
"actor='$actor', " +
|
|
||||||
"id='$id', " +
|
|
||||||
"to=$to, " +
|
|
||||||
"cc=$cc" +
|
|
||||||
")" +
|
|
||||||
" ${super.toString()}"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,78 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.activitypub.domain.model
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty
|
|
||||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize
|
|
||||||
import dev.usbharu.hideout.activitypub.domain.model.objects.Object
|
|
||||||
import dev.usbharu.hideout.activitypub.domain.model.objects.ObjectDeserializer
|
|
||||||
|
|
||||||
open class Delete : Object, HasId, HasActor {
|
|
||||||
@JsonDeserialize(using = ObjectDeserializer::class)
|
|
||||||
@JsonProperty("object")
|
|
||||||
val apObject: Object
|
|
||||||
val published: String
|
|
||||||
override var actor: String = ""
|
|
||||||
override var id: String = ""
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
type: List<String> = emptyList(),
|
|
||||||
actor: String,
|
|
||||||
id: String,
|
|
||||||
`object`: Object,
|
|
||||||
published: String
|
|
||||||
) : super(add(type, "Delete")) {
|
|
||||||
this.apObject = `object`
|
|
||||||
this.published = published
|
|
||||||
this.actor = actor
|
|
||||||
this.id = id
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
|
||||||
if (this === other) return true
|
|
||||||
if (javaClass != other?.javaClass) return false
|
|
||||||
if (!super.equals(other)) return false
|
|
||||||
|
|
||||||
other as Delete
|
|
||||||
|
|
||||||
if (apObject != other.apObject) return false
|
|
||||||
if (published != other.published) return false
|
|
||||||
if (actor != other.actor) return false
|
|
||||||
if (id != other.id) return false
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
|
||||||
var result = super.hashCode()
|
|
||||||
result = 31 * result + apObject.hashCode()
|
|
||||||
result = 31 * result + published.hashCode()
|
|
||||||
result = 31 * result + actor.hashCode()
|
|
||||||
result = 31 * result + id.hashCode()
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toString(): String {
|
|
||||||
return "Delete(" +
|
|
||||||
"apObject=$apObject, " +
|
|
||||||
"published='$published', " +
|
|
||||||
"actor='$actor', " +
|
|
||||||
"id='$id'" +
|
|
||||||
")" +
|
|
||||||
" ${super.toString()}"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,57 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.activitypub.domain.model
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonSetter
|
|
||||||
import com.fasterxml.jackson.annotation.Nulls
|
|
||||||
import dev.usbharu.hideout.activitypub.domain.model.objects.Object
|
|
||||||
|
|
||||||
open class Document(
|
|
||||||
type: List<String> = emptyList(),
|
|
||||||
@JsonSetter(nulls = Nulls.AS_EMPTY)
|
|
||||||
override val name: String = "",
|
|
||||||
val mediaType: String,
|
|
||||||
val url: String
|
|
||||||
) : Object(
|
|
||||||
type = add(type, "Document")
|
|
||||||
),
|
|
||||||
HasName {
|
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
|
||||||
if (this === other) return true
|
|
||||||
if (javaClass != other?.javaClass) return false
|
|
||||||
if (!super.equals(other)) return false
|
|
||||||
|
|
||||||
other as Document
|
|
||||||
|
|
||||||
if (mediaType != other.mediaType) return false
|
|
||||||
if (url != other.url) return false
|
|
||||||
if (name != other.name) return false
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
|
||||||
var result = super.hashCode()
|
|
||||||
result = 31 * result + mediaType.hashCode()
|
|
||||||
result = 31 * result + url.hashCode()
|
|
||||||
result = 31 * result + name.hashCode()
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toString(): String = "Document(mediaType=$mediaType, url=$url, name='$name') ${super.toString()}"
|
|
||||||
}
|
|
|
@ -1,66 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.activitypub.domain.model
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.activitypub.domain.model.objects.Object
|
|
||||||
|
|
||||||
open class Emoji(
|
|
||||||
type: List<String>,
|
|
||||||
override val name: String,
|
|
||||||
override val id: String,
|
|
||||||
val updated: String,
|
|
||||||
val icon: Image
|
|
||||||
) : Object(
|
|
||||||
type = add(type, "Emoji")
|
|
||||||
),
|
|
||||||
HasName,
|
|
||||||
HasId {
|
|
||||||
|
|
||||||
override fun toString(): String {
|
|
||||||
return "Emoji(" +
|
|
||||||
"name='$name', " +
|
|
||||||
"id='$id', " +
|
|
||||||
"updated='$updated', " +
|
|
||||||
"icon=$icon" +
|
|
||||||
")" +
|
|
||||||
" ${super.toString()}"
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
|
||||||
if (this === other) return true
|
|
||||||
if (javaClass != other?.javaClass) return false
|
|
||||||
if (!super.equals(other)) return false
|
|
||||||
|
|
||||||
other as Emoji
|
|
||||||
|
|
||||||
if (name != other.name) return false
|
|
||||||
if (id != other.id) return false
|
|
||||||
if (updated != other.updated) return false
|
|
||||||
if (icon != other.icon) return false
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
|
||||||
var result = super.hashCode()
|
|
||||||
result = 31 * result + name.hashCode()
|
|
||||||
result = 31 * result + id.hashCode()
|
|
||||||
result = 31 * result + updated.hashCode()
|
|
||||||
result = 31 * result + icon.hashCode()
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,61 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.activitypub.domain.model
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty
|
|
||||||
import dev.usbharu.hideout.activitypub.domain.model.objects.Object
|
|
||||||
|
|
||||||
open class Follow(
|
|
||||||
type: List<String> = emptyList(),
|
|
||||||
@JsonProperty("object") val apObject: String,
|
|
||||||
override val actor: String,
|
|
||||||
val id: String? = null
|
|
||||||
) : Object(
|
|
||||||
type = add(type, "Follow")
|
|
||||||
),
|
|
||||||
HasActor {
|
|
||||||
override fun equals(other: Any?): Boolean {
|
|
||||||
if (this === other) return true
|
|
||||||
if (javaClass != other?.javaClass) return false
|
|
||||||
if (!super.equals(other)) return false
|
|
||||||
|
|
||||||
other as Follow
|
|
||||||
|
|
||||||
if (apObject != other.apObject) return false
|
|
||||||
if (actor != other.actor) return false
|
|
||||||
if (id != other.id) return false
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
|
||||||
var result = super.hashCode()
|
|
||||||
result = 31 * result + apObject.hashCode()
|
|
||||||
result = 31 * result + actor.hashCode()
|
|
||||||
result = 31 * result + (id?.hashCode() ?: 0)
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toString(): String {
|
|
||||||
return "Follow(" +
|
|
||||||
"apObject='$apObject', " +
|
|
||||||
"actor='$actor', " +
|
|
||||||
"id=$id" +
|
|
||||||
")" +
|
|
||||||
" ${super.toString()}"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,55 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.activitypub.domain.model
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.activitypub.domain.model.objects.Object
|
|
||||||
|
|
||||||
open class Image(
|
|
||||||
type: List<String> = emptyList(),
|
|
||||||
val mediaType: String? = null,
|
|
||||||
val url: String
|
|
||||||
) : Object(
|
|
||||||
add(type, "Image")
|
|
||||||
) {
|
|
||||||
override fun equals(other: Any?): Boolean {
|
|
||||||
if (this === other) return true
|
|
||||||
if (javaClass != other?.javaClass) return false
|
|
||||||
if (!super.equals(other)) return false
|
|
||||||
|
|
||||||
other as Image
|
|
||||||
|
|
||||||
if (mediaType != other.mediaType) return false
|
|
||||||
if (url != other.url) return false
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
|
||||||
var result = super.hashCode()
|
|
||||||
result = 31 * result + (mediaType?.hashCode() ?: 0)
|
|
||||||
result = 31 * result + url.hashCode()
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toString(): String {
|
|
||||||
return "Image(" +
|
|
||||||
"mediaType=$mediaType, " +
|
|
||||||
"url='$url'" +
|
|
||||||
")" +
|
|
||||||
" ${super.toString()}"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,101 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.activitypub.domain.model
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonAutoDetect
|
|
||||||
import com.fasterxml.jackson.annotation.JsonCreator
|
|
||||||
import com.fasterxml.jackson.annotation.JsonInclude
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty
|
|
||||||
import com.fasterxml.jackson.core.JsonGenerator
|
|
||||||
import com.fasterxml.jackson.databind.JsonDeserializer
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode
|
|
||||||
import com.fasterxml.jackson.databind.JsonSerializer
|
|
||||||
import com.fasterxml.jackson.databind.SerializerProvider
|
|
||||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize
|
|
||||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize
|
|
||||||
|
|
||||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
|
|
||||||
open class JsonLd {
|
|
||||||
@JsonProperty("@context")
|
|
||||||
@JsonDeserialize(contentUsing = StringOrObjectDeserializer::class)
|
|
||||||
@JsonSerialize(include = JsonSerialize.Inclusion.NON_EMPTY, using = ContextSerializer::class)
|
|
||||||
@JsonInclude(JsonInclude.Include.NON_EMPTY)
|
|
||||||
var context: List<StringOrObject> = emptyList()
|
|
||||||
set(value) {
|
|
||||||
field = value.filterNot { it.isEmpty() }
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonCreator
|
|
||||||
constructor(context: List<StringOrObject?>?) {
|
|
||||||
if (context != null) {
|
|
||||||
this.context = context.filterNotNull().filterNot { it.isEmpty() }
|
|
||||||
} else {
|
|
||||||
this.context = emptyList()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected constructor()
|
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
|
||||||
if (this === other) return true
|
|
||||||
if (other !is JsonLd) return false
|
|
||||||
|
|
||||||
return context == other.context
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun hashCode(): Int = context.hashCode()
|
|
||||||
|
|
||||||
override fun toString(): String = "JsonLd(context=$context)"
|
|
||||||
}
|
|
||||||
|
|
||||||
class ContextDeserializer : JsonDeserializer<String>() {
|
|
||||||
|
|
||||||
override fun deserialize(
|
|
||||||
p0: com.fasterxml.jackson.core.JsonParser?,
|
|
||||||
p1: com.fasterxml.jackson.databind.DeserializationContext?
|
|
||||||
): String {
|
|
||||||
val readTree: JsonNode = p0?.codec?.readTree(p0) ?: return ""
|
|
||||||
if (readTree.isValueNode) {
|
|
||||||
return readTree.textValue()
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ContextSerializer : JsonSerializer<List<StringOrObject>>() {
|
|
||||||
|
|
||||||
@Deprecated("Deprecated in Java")
|
|
||||||
override fun isEmpty(value: List<StringOrObject>?): Boolean = value.isNullOrEmpty()
|
|
||||||
|
|
||||||
override fun isEmpty(provider: SerializerProvider?, value: List<StringOrObject>?): Boolean = value.isNullOrEmpty()
|
|
||||||
|
|
||||||
override fun serialize(value: List<StringOrObject>?, gen: JsonGenerator?, serializers: SerializerProvider) {
|
|
||||||
if (value.isNullOrEmpty()) {
|
|
||||||
serializers.defaultSerializeNull(gen)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (value.size == 1) {
|
|
||||||
serializers.findValueSerializer(StringOrObject::class.java).serialize(value[0], gen, serializers)
|
|
||||||
} else {
|
|
||||||
gen?.writeStartArray()
|
|
||||||
value.forEach {
|
|
||||||
serializers.findValueSerializer(StringOrObject::class.java).serialize(it, gen, serializers)
|
|
||||||
}
|
|
||||||
gen?.writeEndArray()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,53 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.activitypub.domain.model
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.activitypub.domain.model.objects.Object
|
|
||||||
|
|
||||||
open class Key(
|
|
||||||
override val id: String,
|
|
||||||
val owner: String,
|
|
||||||
val publicKeyPem: String
|
|
||||||
) : Object(
|
|
||||||
type = add(list = emptyList(), type = "Key")
|
|
||||||
),
|
|
||||||
HasId {
|
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
|
||||||
if (this === other) return true
|
|
||||||
if (javaClass != other?.javaClass) return false
|
|
||||||
if (!super.equals(other)) return false
|
|
||||||
|
|
||||||
other as Key
|
|
||||||
|
|
||||||
if (owner != other.owner) return false
|
|
||||||
if (publicKeyPem != other.publicKeyPem) return false
|
|
||||||
if (id != other.id) return false
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
|
||||||
var result = super.hashCode()
|
|
||||||
result = 31 * result + owner.hashCode()
|
|
||||||
result = 31 * result + publicKeyPem.hashCode()
|
|
||||||
result = 31 * result + id.hashCode()
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toString(): String = "Key(owner=$owner, publicKeyPem=$publicKeyPem, id='$id') ${super.toString()}"
|
|
||||||
}
|
|
|
@ -1,73 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.activitypub.domain.model
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty
|
|
||||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize
|
|
||||||
import dev.usbharu.hideout.activitypub.domain.model.objects.Object
|
|
||||||
import dev.usbharu.hideout.activitypub.domain.model.objects.ObjectDeserializer
|
|
||||||
|
|
||||||
open class Like(
|
|
||||||
type: List<String> = emptyList(),
|
|
||||||
override val actor: String,
|
|
||||||
override val id: String,
|
|
||||||
@JsonProperty("object") val apObject: String,
|
|
||||||
val content: String,
|
|
||||||
@JsonDeserialize(contentUsing = ObjectDeserializer::class) val tag: List<Object> = emptyList()
|
|
||||||
) : Object(
|
|
||||||
type = add(type, "Like")
|
|
||||||
),
|
|
||||||
HasId,
|
|
||||||
HasActor {
|
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
|
||||||
if (this === other) return true
|
|
||||||
if (javaClass != other?.javaClass) return false
|
|
||||||
if (!super.equals(other)) return false
|
|
||||||
|
|
||||||
other as Like
|
|
||||||
|
|
||||||
if (actor != other.actor) return false
|
|
||||||
if (id != other.id) return false
|
|
||||||
if (apObject != other.apObject) return false
|
|
||||||
if (content != other.content) return false
|
|
||||||
if (tag != other.tag) return false
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
|
||||||
var result = super.hashCode()
|
|
||||||
result = 31 * result + actor.hashCode()
|
|
||||||
result = 31 * result + id.hashCode()
|
|
||||||
result = 31 * result + apObject.hashCode()
|
|
||||||
result = 31 * result + content.hashCode()
|
|
||||||
result = 31 * result + tag.hashCode()
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toString(): String {
|
|
||||||
return "Like(" +
|
|
||||||
"actor='$actor', " +
|
|
||||||
"id='$id', " +
|
|
||||||
"apObject='$apObject', " +
|
|
||||||
"content='$content', " +
|
|
||||||
"tag=$tag" +
|
|
||||||
")" +
|
|
||||||
" ${super.toString()}"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,110 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.activitypub.domain.model
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty
|
|
||||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize
|
|
||||||
import dev.usbharu.hideout.activitypub.domain.model.objects.Object
|
|
||||||
import dev.usbharu.hideout.activitypub.domain.model.objects.ObjectDeserializer
|
|
||||||
|
|
||||||
open class Note
|
|
||||||
@Suppress("LongParameterList", "CyclomaticComplexMethod")
|
|
||||||
constructor(
|
|
||||||
type: List<String> = emptyList(),
|
|
||||||
override val id: String,
|
|
||||||
val attributedTo: String,
|
|
||||||
val content: String,
|
|
||||||
val published: String,
|
|
||||||
val to: List<String> = emptyList(),
|
|
||||||
val cc: List<String> = emptyList(),
|
|
||||||
val sensitive: Boolean = false,
|
|
||||||
val inReplyTo: String? = null,
|
|
||||||
val attachment: List<Document> = emptyList(),
|
|
||||||
@JsonDeserialize(contentUsing = ObjectDeserializer::class)
|
|
||||||
val tag: List<Object> = emptyList(),
|
|
||||||
val quoteUri: String? = null,
|
|
||||||
val quoteUrl: String? = null,
|
|
||||||
@JsonProperty("_misskey_quote")
|
|
||||||
val misskeyQuote: String? = null
|
|
||||||
) : Object(
|
|
||||||
type = add(type, "Note")
|
|
||||||
),
|
|
||||||
HasId {
|
|
||||||
|
|
||||||
@Suppress("CyclomaticComplexMethod", "CognitiveComplexMethod")
|
|
||||||
override fun equals(other: Any?): Boolean {
|
|
||||||
if (this === other) return true
|
|
||||||
if (javaClass != other?.javaClass) return false
|
|
||||||
if (!super.equals(other)) return false
|
|
||||||
|
|
||||||
other as Note
|
|
||||||
|
|
||||||
if (id != other.id) return false
|
|
||||||
if (attributedTo != other.attributedTo) return false
|
|
||||||
if (content != other.content) return false
|
|
||||||
if (published != other.published) return false
|
|
||||||
if (to != other.to) return false
|
|
||||||
if (cc != other.cc) return false
|
|
||||||
if (sensitive != other.sensitive) return false
|
|
||||||
if (inReplyTo != other.inReplyTo) return false
|
|
||||||
if (attachment != other.attachment) return false
|
|
||||||
if (tag != other.tag) return false
|
|
||||||
if (quoteUri != other.quoteUri) return false
|
|
||||||
if (quoteUrl != other.quoteUrl) return false
|
|
||||||
if (misskeyQuote != other.misskeyQuote) return false
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("CyclomaticComplexMethod")
|
|
||||||
override fun hashCode(): Int {
|
|
||||||
var result = super.hashCode()
|
|
||||||
result = 31 * result + id.hashCode()
|
|
||||||
result = 31 * result + attributedTo.hashCode()
|
|
||||||
result = 31 * result + content.hashCode()
|
|
||||||
result = 31 * result + published.hashCode()
|
|
||||||
result = 31 * result + to.hashCode()
|
|
||||||
result = 31 * result + cc.hashCode()
|
|
||||||
result = 31 * result + sensitive.hashCode()
|
|
||||||
result = 31 * result + (inReplyTo?.hashCode() ?: 0)
|
|
||||||
result = 31 * result + attachment.hashCode()
|
|
||||||
result = 31 * result + tag.hashCode()
|
|
||||||
result = 31 * result + (quoteUri?.hashCode() ?: 0)
|
|
||||||
result = 31 * result + (quoteUrl?.hashCode() ?: 0)
|
|
||||||
result = 31 * result + (misskeyQuote?.hashCode() ?: 0)
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toString(): String {
|
|
||||||
return "Note(" +
|
|
||||||
"id='$id', " +
|
|
||||||
"attributedTo='$attributedTo', " +
|
|
||||||
"content='$content', " +
|
|
||||||
"published='$published', " +
|
|
||||||
"to=$to, " +
|
|
||||||
"cc=$cc, " +
|
|
||||||
"sensitive=$sensitive, " +
|
|
||||||
"inReplyTo=$inReplyTo, " +
|
|
||||||
"attachment=$attachment, " +
|
|
||||||
"tag=$tag, " +
|
|
||||||
"quoteUri=$quoteUri, " +
|
|
||||||
"quoteUrl=$quoteUrl, " +
|
|
||||||
"misskeyQuote=$misskeyQuote" +
|
|
||||||
")" +
|
|
||||||
" ${super.toString()}"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,102 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.activitypub.domain.model
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.activitypub.domain.model.objects.Object
|
|
||||||
|
|
||||||
open class Person
|
|
||||||
@Suppress("LongParameterList")
|
|
||||||
constructor(
|
|
||||||
type: List<String> = emptyList(),
|
|
||||||
val name: String?,
|
|
||||||
override val id: String,
|
|
||||||
var preferredUsername: String,
|
|
||||||
var summary: String?,
|
|
||||||
var inbox: String,
|
|
||||||
var outbox: String,
|
|
||||||
var url: String,
|
|
||||||
private var icon: Image?,
|
|
||||||
var publicKey: Key,
|
|
||||||
var endpoints: Map<String, String> = emptyMap(),
|
|
||||||
var followers: String?,
|
|
||||||
var following: String?,
|
|
||||||
val manuallyApprovesFollowers: Boolean? = false
|
|
||||||
) : Object(add(type, "Person")), HasId {
|
|
||||||
|
|
||||||
@Suppress("CyclomaticComplexMethod", "CognitiveComplexMethod")
|
|
||||||
override fun equals(other: Any?): Boolean {
|
|
||||||
if (this === other) return true
|
|
||||||
if (javaClass != other?.javaClass) return false
|
|
||||||
if (!super.equals(other)) return false
|
|
||||||
|
|
||||||
other as Person
|
|
||||||
|
|
||||||
if (name != other.name) return false
|
|
||||||
if (id != other.id) return false
|
|
||||||
if (preferredUsername != other.preferredUsername) return false
|
|
||||||
if (summary != other.summary) return false
|
|
||||||
if (inbox != other.inbox) return false
|
|
||||||
if (outbox != other.outbox) return false
|
|
||||||
if (url != other.url) return false
|
|
||||||
if (icon != other.icon) return false
|
|
||||||
if (publicKey != other.publicKey) return false
|
|
||||||
if (endpoints != other.endpoints) return false
|
|
||||||
if (followers != other.followers) return false
|
|
||||||
if (following != other.following) return false
|
|
||||||
if (manuallyApprovesFollowers != other.manuallyApprovesFollowers) return false
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("CyclomaticComplexMethod")
|
|
||||||
override fun hashCode(): Int {
|
|
||||||
var result = super.hashCode()
|
|
||||||
result = 31 * result + (name?.hashCode() ?: 0)
|
|
||||||
result = 31 * result + id.hashCode()
|
|
||||||
result = 31 * result + preferredUsername.hashCode()
|
|
||||||
result = 31 * result + (summary?.hashCode() ?: 0)
|
|
||||||
result = 31 * result + inbox.hashCode()
|
|
||||||
result = 31 * result + outbox.hashCode()
|
|
||||||
result = 31 * result + url.hashCode()
|
|
||||||
result = 31 * result + (icon?.hashCode() ?: 0)
|
|
||||||
result = 31 * result + publicKey.hashCode()
|
|
||||||
result = 31 * result + endpoints.hashCode()
|
|
||||||
result = 31 * result + (followers?.hashCode() ?: 0)
|
|
||||||
result = 31 * result + (following?.hashCode() ?: 0)
|
|
||||||
result = 31 * result + (manuallyApprovesFollowers?.hashCode() ?: 0)
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toString(): String {
|
|
||||||
return "Person(" +
|
|
||||||
"name=$name, " +
|
|
||||||
"id='$id', " +
|
|
||||||
"preferredUsername='$preferredUsername', " +
|
|
||||||
"summary=$summary, " +
|
|
||||||
"inbox='$inbox', " +
|
|
||||||
"outbox='$outbox', " +
|
|
||||||
"url='$url', " +
|
|
||||||
"icon=$icon, " +
|
|
||||||
"publicKey=$publicKey, " +
|
|
||||||
"endpoints=$endpoints, " +
|
|
||||||
"followers=$followers, " +
|
|
||||||
"following=$following, " +
|
|
||||||
"manuallyApprovesFollowers=$manuallyApprovesFollowers" +
|
|
||||||
")" +
|
|
||||||
" ${super.toString()}"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,59 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.activitypub.domain.model
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty
|
|
||||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize
|
|
||||||
import dev.usbharu.hideout.activitypub.domain.model.objects.Object
|
|
||||||
import dev.usbharu.hideout.activitypub.domain.model.objects.ObjectDeserializer
|
|
||||||
|
|
||||||
open class Reject(
|
|
||||||
override val actor: String,
|
|
||||||
override val id: String,
|
|
||||||
@JsonDeserialize(using = ObjectDeserializer::class) @JsonProperty("object") val apObject: Object
|
|
||||||
) : Object(listOf("Reject")), HasId, HasActor {
|
|
||||||
override fun equals(other: Any?): Boolean {
|
|
||||||
if (this === other) return true
|
|
||||||
if (javaClass != other?.javaClass) return false
|
|
||||||
if (!super.equals(other)) return false
|
|
||||||
|
|
||||||
other as Reject
|
|
||||||
|
|
||||||
if (actor != other.actor) return false
|
|
||||||
if (id != other.id) return false
|
|
||||||
if (apObject != other.apObject) return false
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
|
||||||
var result = super.hashCode()
|
|
||||||
result = 31 * result + actor.hashCode()
|
|
||||||
result = 31 * result + id.hashCode()
|
|
||||||
result = 31 * result + apObject.hashCode()
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toString(): String {
|
|
||||||
return "Reject(" +
|
|
||||||
"actor='$actor', " +
|
|
||||||
"id='$id', " +
|
|
||||||
"apObject=$apObject" +
|
|
||||||
")" +
|
|
||||||
" ${super.toString()}"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,76 +0,0 @@
|
||||||
package dev.usbharu.hideout.activitypub.domain.model
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonCreator
|
|
||||||
import com.fasterxml.jackson.core.JsonGenerator
|
|
||||||
import com.fasterxml.jackson.core.JsonParser
|
|
||||||
import com.fasterxml.jackson.core.type.TypeReference
|
|
||||||
import com.fasterxml.jackson.databind.*
|
|
||||||
|
|
||||||
open class StringOrObject {
|
|
||||||
var contextString: String? = null
|
|
||||||
var contextObject: Map<String, String>? = null
|
|
||||||
|
|
||||||
@JsonCreator
|
|
||||||
protected constructor()
|
|
||||||
|
|
||||||
constructor(string: String) : this() {
|
|
||||||
contextString = string
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(contextObject: Map<String, String>) : this() {
|
|
||||||
this.contextObject = contextObject
|
|
||||||
}
|
|
||||||
|
|
||||||
fun isEmpty(): Boolean = contextString.isNullOrEmpty() and contextObject.isNullOrEmpty()
|
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
|
||||||
if (this === other) return true
|
|
||||||
if (javaClass != other?.javaClass) return false
|
|
||||||
|
|
||||||
other as StringOrObject
|
|
||||||
|
|
||||||
if (contextString != other.contextString) return false
|
|
||||||
if (contextObject != other.contextObject) return false
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
|
||||||
var result = contextString?.hashCode() ?: 0
|
|
||||||
result = 31 * result + (contextObject?.hashCode() ?: 0)
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toString(): String = "StringOrObject(contextString=$contextString, contextObject=$contextObject)"
|
|
||||||
}
|
|
||||||
|
|
||||||
class StringOrObjectDeserializer : JsonDeserializer<StringOrObject>() {
|
|
||||||
override fun deserialize(p: JsonParser?, ctxt: DeserializationContext): StringOrObject {
|
|
||||||
val readTree: JsonNode = p?.codec?.readTree(p) ?: return StringOrObject("")
|
|
||||||
return if (readTree.isValueNode) {
|
|
||||||
StringOrObject(readTree.textValue())
|
|
||||||
} else if (readTree.isObject) {
|
|
||||||
val map: Map<String, String> = ctxt.readTreeAsValue(
|
|
||||||
readTree,
|
|
||||||
ctxt.typeFactory.constructType(object : TypeReference<Map<String, String>>() {})
|
|
||||||
)
|
|
||||||
StringOrObject(map)
|
|
||||||
} else {
|
|
||||||
StringOrObject("")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class StringORObjectSerializer : JsonSerializer<StringOrObject>() {
|
|
||||||
override fun serialize(value: StringOrObject?, gen: JsonGenerator?, serializers: SerializerProvider) {
|
|
||||||
if (value == null) {
|
|
||||||
serializers.defaultSerializeNull(gen)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (value.contextString != null) {
|
|
||||||
gen?.writeString(value.contextString)
|
|
||||||
} else {
|
|
||||||
serializers.defaultSerializeValue(value.contextObject, gen)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,41 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.activitypub.domain.model
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.activitypub.domain.model.objects.Object
|
|
||||||
|
|
||||||
open class Tombstone(type: List<String> = emptyList(), override val id: String) :
|
|
||||||
Object(add(type, "Tombstone")),
|
|
||||||
HasId {
|
|
||||||
override fun equals(other: Any?): Boolean {
|
|
||||||
if (this === other) return true
|
|
||||||
if (javaClass != other?.javaClass) return false
|
|
||||||
if (!super.equals(other)) return false
|
|
||||||
|
|
||||||
other as Tombstone
|
|
||||||
|
|
||||||
return id == other.id
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
|
||||||
var result = super.hashCode()
|
|
||||||
result = 31 * result + id.hashCode()
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toString(): String = "Tombstone(id='$id') ${super.toString()}"
|
|
||||||
}
|
|
|
@ -1,66 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.activitypub.domain.model
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty
|
|
||||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize
|
|
||||||
import dev.usbharu.hideout.activitypub.domain.model.objects.Object
|
|
||||||
import dev.usbharu.hideout.activitypub.domain.model.objects.ObjectDeserializer
|
|
||||||
|
|
||||||
open class Undo(
|
|
||||||
type: List<String> = emptyList(),
|
|
||||||
override val actor: String,
|
|
||||||
override val id: String,
|
|
||||||
@JsonDeserialize(using = ObjectDeserializer::class)
|
|
||||||
@JsonProperty("object") val apObject: Object,
|
|
||||||
val published: String?
|
|
||||||
) : Object(add(type, "Undo")), HasId, HasActor {
|
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
|
||||||
if (this === other) return true
|
|
||||||
if (javaClass != other?.javaClass) return false
|
|
||||||
if (!super.equals(other)) return false
|
|
||||||
|
|
||||||
other as Undo
|
|
||||||
|
|
||||||
if (actor != other.actor) return false
|
|
||||||
if (id != other.id) return false
|
|
||||||
if (apObject != other.apObject) return false
|
|
||||||
if (published != other.published) return false
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
|
||||||
var result = super.hashCode()
|
|
||||||
result = 31 * result + actor.hashCode()
|
|
||||||
result = 31 * result + id.hashCode()
|
|
||||||
result = 31 * result + apObject.hashCode()
|
|
||||||
result = 31 * result + (published?.hashCode() ?: 0)
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toString(): String {
|
|
||||||
return "Undo(" +
|
|
||||||
"actor='$actor', " +
|
|
||||||
"id='$id', " +
|
|
||||||
"apObject=$apObject, " +
|
|
||||||
"published=$published" +
|
|
||||||
")" +
|
|
||||||
" ${super.toString()}"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,67 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
@file:Suppress("ClassName")
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.activitypub.domain.model.nodeinfo
|
|
||||||
|
|
||||||
@Suppress("ClassNaming")
|
|
||||||
data class Nodeinfo2_0(
|
|
||||||
val version: String,
|
|
||||||
val software: Software,
|
|
||||||
val protocols: List<String>,
|
|
||||||
val services: Services,
|
|
||||||
val openRegistrations: Boolean,
|
|
||||||
val usage: Usage,
|
|
||||||
val metadata: Metadata
|
|
||||||
) {
|
|
||||||
data class Software(
|
|
||||||
val name: String,
|
|
||||||
val version: String
|
|
||||||
)
|
|
||||||
|
|
||||||
data class Services(
|
|
||||||
val inbound: List<String>,
|
|
||||||
val outbound: List<String>
|
|
||||||
)
|
|
||||||
|
|
||||||
data class Usage(
|
|
||||||
val users: Users,
|
|
||||||
val localPosts: Int,
|
|
||||||
val localComments: Int
|
|
||||||
) {
|
|
||||||
data class Users(
|
|
||||||
val total: Int,
|
|
||||||
val activeHalfYear: Int,
|
|
||||||
val activeMonth: Int
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
data class Metadata(
|
|
||||||
val nodeName: String,
|
|
||||||
val nodeDescription: String,
|
|
||||||
val maintainer: Maintainer,
|
|
||||||
val langs: List<String>,
|
|
||||||
val tosUrl: String,
|
|
||||||
val repositoryUrl: String,
|
|
||||||
val feedbackUrl: String,
|
|
||||||
) {
|
|
||||||
data class Maintainer(
|
|
||||||
val name: String,
|
|
||||||
val email: String
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,77 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.activitypub.domain.model.objects
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.JsonGenerator
|
|
||||||
import com.fasterxml.jackson.databind.JsonSerializer
|
|
||||||
import com.fasterxml.jackson.databind.SerializerProvider
|
|
||||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize
|
|
||||||
import dev.usbharu.hideout.activitypub.domain.model.JsonLd
|
|
||||||
|
|
||||||
open class Object : JsonLd {
|
|
||||||
@JsonSerialize(using = TypeSerializer::class)
|
|
||||||
var type: List<String> = emptyList()
|
|
||||||
set(value) {
|
|
||||||
field = value.filter { it.isNotBlank() }
|
|
||||||
}
|
|
||||||
|
|
||||||
protected constructor()
|
|
||||||
constructor(type: List<String>) : super() {
|
|
||||||
this.type = type.filter { it.isNotBlank() }
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
|
||||||
if (this === other) return true
|
|
||||||
if (javaClass != other?.javaClass) return false
|
|
||||||
if (!super.equals(other)) return false
|
|
||||||
|
|
||||||
other as Object
|
|
||||||
|
|
||||||
return type == other.type
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
|
||||||
var result = super.hashCode()
|
|
||||||
result = 31 * result + type.hashCode()
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toString(): String = "Object(type=$type) ${super.toString()}"
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
@JvmStatic
|
|
||||||
protected fun add(list: List<String>, type: String): List<String> {
|
|
||||||
val toMutableList = list.toMutableList()
|
|
||||||
toMutableList.add(type)
|
|
||||||
return toMutableList.distinct()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class TypeSerializer : JsonSerializer<List<String>>() {
|
|
||||||
override fun serialize(value: List<String>?, gen: JsonGenerator?, serializers: SerializerProvider?) {
|
|
||||||
if (value?.size == 1) {
|
|
||||||
gen?.writeString(value[0])
|
|
||||||
} else {
|
|
||||||
gen?.writeStartArray()
|
|
||||||
value?.forEach {
|
|
||||||
gen?.writeString(it)
|
|
||||||
}
|
|
||||||
gen?.writeEndArray()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,110 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.activitypub.domain.model.objects
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.JsonParser
|
|
||||||
import com.fasterxml.jackson.databind.DeserializationContext
|
|
||||||
import com.fasterxml.jackson.databind.JsonDeserializer
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode
|
|
||||||
import dev.usbharu.hideout.activitypub.domain.model.*
|
|
||||||
import dev.usbharu.hideout.activitypub.service.common.ExtendedActivityVocabulary
|
|
||||||
|
|
||||||
class ObjectDeserializer : JsonDeserializer<Object>() {
|
|
||||||
@Suppress("LongMethod", "CyclomaticComplexMethod")
|
|
||||||
override fun deserialize(p: JsonParser?, ctxt: DeserializationContext?): Object? {
|
|
||||||
requireNotNull(p)
|
|
||||||
val treeNode: JsonNode = requireNotNull(p.codec?.readTree(p))
|
|
||||||
if (treeNode.isValueNode) {
|
|
||||||
return ObjectValue(
|
|
||||||
emptyList(),
|
|
||||||
treeNode.asText()
|
|
||||||
)
|
|
||||||
} else if (treeNode.isObject) {
|
|
||||||
val type = treeNode["type"]
|
|
||||||
val activityType = if (type.isArray) {
|
|
||||||
type.firstNotNullOf { jsonNode: JsonNode ->
|
|
||||||
ExtendedActivityVocabulary.values().firstOrNull { it.name.equals(jsonNode.asText(), true) }
|
|
||||||
}
|
|
||||||
} else if (type.isValueNode) {
|
|
||||||
ExtendedActivityVocabulary.values().firstOrNull { it.name.equals(type.asText(), true) }
|
|
||||||
} else {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
|
|
||||||
return when (activityType) {
|
|
||||||
ExtendedActivityVocabulary.Follow -> p.codec.treeToValue(treeNode, Follow::class.java)
|
|
||||||
ExtendedActivityVocabulary.Note -> p.codec.treeToValue(treeNode, Note::class.java)
|
|
||||||
ExtendedActivityVocabulary.Object -> p.codec.treeToValue(treeNode, Object::class.java)
|
|
||||||
ExtendedActivityVocabulary.Link -> null
|
|
||||||
ExtendedActivityVocabulary.Activity -> null
|
|
||||||
ExtendedActivityVocabulary.IntransitiveActivity -> null
|
|
||||||
ExtendedActivityVocabulary.Collection -> null
|
|
||||||
ExtendedActivityVocabulary.OrderedCollection -> null
|
|
||||||
ExtendedActivityVocabulary.CollectionPage -> null
|
|
||||||
ExtendedActivityVocabulary.OrderedCollectionPage -> null
|
|
||||||
ExtendedActivityVocabulary.Accept -> p.codec.treeToValue(treeNode, Accept::class.java)
|
|
||||||
ExtendedActivityVocabulary.Add -> null
|
|
||||||
ExtendedActivityVocabulary.Announce -> p.codec.treeToValue(treeNode, Announce::class.java)
|
|
||||||
ExtendedActivityVocabulary.Arrive -> null
|
|
||||||
ExtendedActivityVocabulary.Block -> null
|
|
||||||
ExtendedActivityVocabulary.Create -> p.codec.treeToValue(treeNode, Create::class.java)
|
|
||||||
ExtendedActivityVocabulary.Delete -> p.codec.treeToValue(treeNode, Delete::class.java)
|
|
||||||
ExtendedActivityVocabulary.Dislike -> null
|
|
||||||
ExtendedActivityVocabulary.Flag -> null
|
|
||||||
ExtendedActivityVocabulary.Ignore -> null
|
|
||||||
ExtendedActivityVocabulary.Invite -> null
|
|
||||||
ExtendedActivityVocabulary.Join -> null
|
|
||||||
ExtendedActivityVocabulary.Leave -> null
|
|
||||||
ExtendedActivityVocabulary.Like -> p.codec.treeToValue(treeNode, Like::class.java)
|
|
||||||
ExtendedActivityVocabulary.Listen -> null
|
|
||||||
ExtendedActivityVocabulary.Move -> null
|
|
||||||
ExtendedActivityVocabulary.Offer -> null
|
|
||||||
ExtendedActivityVocabulary.Question -> null
|
|
||||||
ExtendedActivityVocabulary.Reject -> p.codec.treeToValue(treeNode, Reject::class.java)
|
|
||||||
ExtendedActivityVocabulary.Read -> null
|
|
||||||
ExtendedActivityVocabulary.Remove -> null
|
|
||||||
ExtendedActivityVocabulary.TentativeReject -> null
|
|
||||||
ExtendedActivityVocabulary.TentativeAccept -> null
|
|
||||||
ExtendedActivityVocabulary.Travel -> null
|
|
||||||
ExtendedActivityVocabulary.Undo -> p.codec.treeToValue(treeNode, Undo::class.java)
|
|
||||||
ExtendedActivityVocabulary.Update -> null
|
|
||||||
ExtendedActivityVocabulary.View -> null
|
|
||||||
ExtendedActivityVocabulary.Application -> null
|
|
||||||
ExtendedActivityVocabulary.Group -> null
|
|
||||||
ExtendedActivityVocabulary.Organization -> null
|
|
||||||
ExtendedActivityVocabulary.Person -> p.codec.treeToValue(treeNode, Person::class.java)
|
|
||||||
ExtendedActivityVocabulary.Service -> null
|
|
||||||
ExtendedActivityVocabulary.Article -> null
|
|
||||||
ExtendedActivityVocabulary.Audio -> null
|
|
||||||
ExtendedActivityVocabulary.Document -> p.codec.treeToValue(treeNode, Document::class.java)
|
|
||||||
ExtendedActivityVocabulary.Event -> null
|
|
||||||
ExtendedActivityVocabulary.Image -> p.codec.treeToValue(treeNode, Image::class.java)
|
|
||||||
ExtendedActivityVocabulary.Page -> null
|
|
||||||
ExtendedActivityVocabulary.Place -> null
|
|
||||||
ExtendedActivityVocabulary.Profile -> null
|
|
||||||
ExtendedActivityVocabulary.Relationship -> null
|
|
||||||
ExtendedActivityVocabulary.Tombstone -> p.codec.treeToValue(treeNode, Tombstone::class.java)
|
|
||||||
ExtendedActivityVocabulary.Video -> null
|
|
||||||
ExtendedActivityVocabulary.Mention -> null
|
|
||||||
ExtendedActivityVocabulary.Emoji -> p.codec.treeToValue(treeNode, Emoji::class.java)
|
|
||||||
null -> null
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,43 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.activitypub.domain.model.objects
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonCreator
|
|
||||||
|
|
||||||
@Suppress("VariableNaming")
|
|
||||||
open class ObjectValue @JsonCreator constructor(type: List<String>, var `object`: String) : Object(
|
|
||||||
type
|
|
||||||
) {
|
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
|
||||||
if (this === other) return true
|
|
||||||
if (javaClass != other?.javaClass) return false
|
|
||||||
if (!super.equals(other)) return false
|
|
||||||
|
|
||||||
other as ObjectValue
|
|
||||||
|
|
||||||
return `object` == other.`object`
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
|
||||||
var result = super.hashCode()
|
|
||||||
result = 31 * result + `object`.hashCode()
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toString(): String = "ObjectValue(`object`='$`object`') ${super.toString()}"
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.activitypub.domain.model.webfinger
|
|
||||||
|
|
||||||
data class WebFinger(val subject: String, val links: List<Link>) {
|
|
||||||
data class Link(val rel: String, val type: String, val href: String)
|
|
||||||
}
|
|
|
@ -1,82 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.activitypub.infrastructure.exposedquery
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.activitypub.domain.model.Announce
|
|
||||||
import dev.usbharu.hideout.activitypub.query.AnnounceQueryService
|
|
||||||
import dev.usbharu.hideout.activitypub.service.objects.note.APNoteServiceImpl
|
|
||||||
import dev.usbharu.hideout.application.infrastructure.exposed.ResultRowMapper
|
|
||||||
import dev.usbharu.hideout.core.domain.model.post.Post
|
|
||||||
import dev.usbharu.hideout.core.domain.model.post.PostRepository
|
|
||||||
import dev.usbharu.hideout.core.domain.model.post.Visibility
|
|
||||||
import dev.usbharu.hideout.core.infrastructure.exposedrepository.Actors
|
|
||||||
import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts
|
|
||||||
import org.jetbrains.exposed.sql.ResultRow
|
|
||||||
import org.jetbrains.exposed.sql.selectAll
|
|
||||||
import org.springframework.stereotype.Repository
|
|
||||||
import java.time.Instant
|
|
||||||
|
|
||||||
@Repository
|
|
||||||
class ExposedAnnounceQueryService(
|
|
||||||
private val postRepository: PostRepository,
|
|
||||||
private val postResultRowMapper: ResultRowMapper<Post>
|
|
||||||
) : AnnounceQueryService {
|
|
||||||
override suspend fun findById(id: Long): Pair<Announce, Post>? {
|
|
||||||
return Posts
|
|
||||||
.leftJoin(Actors)
|
|
||||||
.selectAll().where { Posts.id eq id }
|
|
||||||
.singleOrNull()
|
|
||||||
?.let { (it.toAnnounce() ?: return null) to (postResultRowMapper.map(it)) }
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun findByApId(apId: String): Pair<Announce, Post>? {
|
|
||||||
return Posts
|
|
||||||
.leftJoin(Actors)
|
|
||||||
.selectAll().where { Posts.apId eq apId }
|
|
||||||
.singleOrNull()
|
|
||||||
?.let { (it.toAnnounce() ?: return null) to (postResultRowMapper.map(it)) }
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun ResultRow.toAnnounce(): Announce? {
|
|
||||||
val repostId = this[Posts.repostId] ?: return null
|
|
||||||
val repost = postRepository.findById(repostId)?.url ?: return null
|
|
||||||
|
|
||||||
val (to, cc) = visibility(
|
|
||||||
Visibility.entries.first { visibility -> visibility.ordinal == this[Posts.visibility] },
|
|
||||||
this[Actors.followers]
|
|
||||||
)
|
|
||||||
|
|
||||||
return Announce(
|
|
||||||
type = emptyList(),
|
|
||||||
id = this[Posts.apId],
|
|
||||||
apObject = repost,
|
|
||||||
actor = this[Actors.url],
|
|
||||||
published = Instant.ofEpochMilli(this[Posts.createdAt]).toString(),
|
|
||||||
to = to,
|
|
||||||
cc = cc
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun visibility(visibility: Visibility, followers: String?): Pair<List<String>, List<String>> {
|
|
||||||
return when (visibility) {
|
|
||||||
Visibility.PUBLIC -> listOf(APNoteServiceImpl.public) to listOf(APNoteServiceImpl.public)
|
|
||||||
Visibility.UNLISTED -> listOfNotNull(followers) to listOf(APNoteServiceImpl.public)
|
|
||||||
Visibility.FOLLOWERS -> listOfNotNull(followers) to listOfNotNull(followers)
|
|
||||||
Visibility.DIRECT -> TODO()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,129 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.activitypub.infrastructure.exposedquery
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.activitypub.domain.model.Document
|
|
||||||
import dev.usbharu.hideout.activitypub.domain.model.Note
|
|
||||||
import dev.usbharu.hideout.activitypub.query.NoteQueryService
|
|
||||||
import dev.usbharu.hideout.activitypub.service.objects.note.APNoteServiceImpl.Companion.public
|
|
||||||
import dev.usbharu.hideout.application.infrastructure.exposed.QueryMapper
|
|
||||||
import dev.usbharu.hideout.core.domain.model.post.Post
|
|
||||||
import dev.usbharu.hideout.core.domain.model.post.PostRepository
|
|
||||||
import dev.usbharu.hideout.core.domain.model.post.Visibility
|
|
||||||
import dev.usbharu.hideout.core.infrastructure.exposedrepository.*
|
|
||||||
import org.jetbrains.exposed.sql.Query
|
|
||||||
import org.jetbrains.exposed.sql.ResultRow
|
|
||||||
import org.jetbrains.exposed.sql.selectAll
|
|
||||||
import org.slf4j.LoggerFactory
|
|
||||||
import org.springframework.stereotype.Repository
|
|
||||||
import java.time.Instant
|
|
||||||
|
|
||||||
@Repository
|
|
||||||
class NoteQueryServiceImpl(private val postRepository: PostRepository, private val postQueryMapper: QueryMapper<Post>) :
|
|
||||||
NoteQueryService {
|
|
||||||
override suspend fun findById(id: Long): Pair<Note, Post>? {
|
|
||||||
return Posts
|
|
||||||
.leftJoin(Actors)
|
|
||||||
.leftJoin(PostsMedia)
|
|
||||||
.leftJoin(Media)
|
|
||||||
.selectAll().where { Posts.id eq id }
|
|
||||||
.let {
|
|
||||||
(it.toNote() ?: return null) to (
|
|
||||||
postQueryMapper.map(it)
|
|
||||||
.singleOrNull() ?: return null
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun findByApid(apId: String): Pair<Note, Post>? {
|
|
||||||
return Posts
|
|
||||||
.leftJoin(Actors)
|
|
||||||
.leftJoin(PostsMedia)
|
|
||||||
.leftJoin(Media)
|
|
||||||
.selectAll().where { Posts.apId eq apId }
|
|
||||||
.let {
|
|
||||||
(it.toNote() ?: return null) to (
|
|
||||||
postQueryMapper.map(it)
|
|
||||||
.singleOrNull() ?: return null
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun ResultRow.toNote(mediaList: List<dev.usbharu.hideout.core.domain.model.media.Media>): Note {
|
|
||||||
val replyId = this[Posts.replyId]
|
|
||||||
val replyTo = if (replyId != null) {
|
|
||||||
val url = postRepository.findById(replyId)?.url
|
|
||||||
if (url == null) {
|
|
||||||
logger.warn("Failed to get replyId: $replyId")
|
|
||||||
}
|
|
||||||
url
|
|
||||||
} else {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
|
|
||||||
val repostId = this[Posts.repostId]
|
|
||||||
val repost = if (repostId != null) {
|
|
||||||
val url = postRepository.findById(repostId)?.url
|
|
||||||
if (url == null) {
|
|
||||||
logger.warn("Failed to get repostId: $repostId")
|
|
||||||
}
|
|
||||||
url
|
|
||||||
} else {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
|
|
||||||
val visibility1 =
|
|
||||||
visibility(
|
|
||||||
Visibility.values().first { visibility -> visibility.ordinal == this[Posts.visibility] },
|
|
||||||
this[Actors.followers]
|
|
||||||
)
|
|
||||||
return Note(
|
|
||||||
id = this[Posts.apId],
|
|
||||||
attributedTo = this[Actors.url],
|
|
||||||
content = this[Posts.text],
|
|
||||||
published = Instant.ofEpochMilli(this[Posts.createdAt]).toString(),
|
|
||||||
to = visibility1.first,
|
|
||||||
cc = visibility1.second,
|
|
||||||
inReplyTo = replyTo,
|
|
||||||
misskeyQuote = repost,
|
|
||||||
quoteUri = repost,
|
|
||||||
quoteUrl = repost,
|
|
||||||
sensitive = this[Posts.sensitive],
|
|
||||||
attachment = mediaList.map { Document(url = it.url, mediaType = "image/jpeg") }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun Query.toNote(): Note? {
|
|
||||||
return this.groupBy { it[Posts.id] }
|
|
||||||
.map { it.value }
|
|
||||||
.map { it.first().toNote(it.mapNotNull { resultRow -> resultRow.toMediaOrNull() }) }
|
|
||||||
.singleOrNull()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun visibility(visibility: Visibility, followers: String?): Pair<List<String>, List<String>> {
|
|
||||||
return when (visibility) {
|
|
||||||
Visibility.PUBLIC -> listOf(public) to listOf(public)
|
|
||||||
Visibility.UNLISTED -> listOfNotNull(followers) to listOf(public)
|
|
||||||
Visibility.FOLLOWERS -> listOfNotNull(followers) to listOfNotNull(followers)
|
|
||||||
Visibility.DIRECT -> TODO()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private val logger = LoggerFactory.getLogger(NoteQueryServiceImpl::class.java)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,29 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.activitypub.interfaces.api.actor
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.activitypub.domain.model.Person
|
|
||||||
import org.springframework.http.ResponseEntity
|
|
||||||
import org.springframework.web.bind.annotation.GetMapping
|
|
||||||
import org.springframework.web.bind.annotation.PathVariable
|
|
||||||
import org.springframework.web.bind.annotation.RestController
|
|
||||||
|
|
||||||
@RestController
|
|
||||||
interface UserAPController {
|
|
||||||
@GetMapping("/users/{username}")
|
|
||||||
suspend fun userAp(@PathVariable("username") username: String): ResponseEntity<Person>
|
|
||||||
}
|
|
|
@ -1,38 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.activitypub.interfaces.api.actor
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.activitypub.domain.Constant
|
|
||||||
import dev.usbharu.hideout.activitypub.domain.model.Person
|
|
||||||
import dev.usbharu.hideout.activitypub.service.objects.user.APUserService
|
|
||||||
import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException
|
|
||||||
import org.springframework.http.HttpStatus
|
|
||||||
import org.springframework.http.ResponseEntity
|
|
||||||
import org.springframework.web.bind.annotation.RestController
|
|
||||||
|
|
||||||
@RestController
|
|
||||||
class UserAPControllerImpl(private val apUserService: APUserService) : UserAPController {
|
|
||||||
override suspend fun userAp(username: String): ResponseEntity<Person> {
|
|
||||||
val person = try {
|
|
||||||
apUserService.getPersonByName(username)
|
|
||||||
} catch (_: UserNotFoundException) {
|
|
||||||
return ResponseEntity.notFound().build()
|
|
||||||
}
|
|
||||||
person.context += Constant.context
|
|
||||||
return ResponseEntity(person, HttpStatus.OK)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,52 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.activitypub.interfaces.api.hostmeta
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.application.config.ApplicationConfig
|
|
||||||
import org.intellij.lang.annotations.Language
|
|
||||||
import org.springframework.http.HttpStatus
|
|
||||||
import org.springframework.http.ResponseEntity
|
|
||||||
import org.springframework.web.bind.annotation.GetMapping
|
|
||||||
import org.springframework.web.bind.annotation.RestController
|
|
||||||
|
|
||||||
@RestController
|
|
||||||
class HostMetaController(private val applicationConfig: ApplicationConfig) {
|
|
||||||
|
|
||||||
val xml = //language=XML
|
|
||||||
"""<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0">
|
|
||||||
<Link rel="lrdd" type="application/xrd+xml"
|
|
||||||
template="${applicationConfig.url}/.well-known/webfinger?resource={uri}"/>
|
|
||||||
</XRD>"""
|
|
||||||
|
|
||||||
@Language("JSON")
|
|
||||||
val json = """{
|
|
||||||
"links": [
|
|
||||||
{
|
|
||||||
"rel": "lrdd",
|
|
||||||
"type": "application/jrd+json",
|
|
||||||
"template": "${applicationConfig.url}/.well-known/webfinger?resource={uri}"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}"""
|
|
||||||
|
|
||||||
@GetMapping("/.well-known/host-meta", produces = ["application/xml"])
|
|
||||||
fun hostmeta(): ResponseEntity<String> = ResponseEntity(xml, HttpStatus.OK)
|
|
||||||
|
|
||||||
@GetMapping("/.well-known/host-meta.json", produces = ["application/json"])
|
|
||||||
fun hostmetJson(): ResponseEntity<String> = ResponseEntity(json, HttpStatus.OK)
|
|
||||||
}
|
|
|
@ -1,38 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.activitypub.interfaces.api.inbox
|
|
||||||
|
|
||||||
import jakarta.servlet.http.HttpServletRequest
|
|
||||||
import org.springframework.http.ResponseEntity
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping
|
|
||||||
import org.springframework.web.bind.annotation.RequestMethod
|
|
||||||
import org.springframework.web.bind.annotation.RestController
|
|
||||||
|
|
||||||
@RestController
|
|
||||||
interface InboxController {
|
|
||||||
@RequestMapping(
|
|
||||||
"/inbox",
|
|
||||||
"/users/{username}/inbox",
|
|
||||||
produces = [
|
|
||||||
"application/activity+json",
|
|
||||||
"application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\""
|
|
||||||
],
|
|
||||||
consumes = ["application/json", "application/*+json"],
|
|
||||||
method = [RequestMethod.POST]
|
|
||||||
)
|
|
||||||
suspend fun inbox(httpServletRequest: HttpServletRequest): ResponseEntity<String>
|
|
||||||
}
|
|
|
@ -1,133 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.activitypub.interfaces.api.inbox
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.activitypub.service.common.APService
|
|
||||||
import dev.usbharu.hideout.core.infrastructure.springframework.httpsignature.HttpSignatureHeaderChecker
|
|
||||||
import dev.usbharu.httpsignature.common.HttpHeaders
|
|
||||||
import dev.usbharu.httpsignature.common.HttpMethod
|
|
||||||
import dev.usbharu.httpsignature.common.HttpRequest
|
|
||||||
import jakarta.servlet.http.HttpServletRequest
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.slf4j.MDCContext
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import org.slf4j.LoggerFactory
|
|
||||||
import org.springframework.http.HttpHeaders.WWW_AUTHENTICATE
|
|
||||||
import org.springframework.http.HttpStatus
|
|
||||||
import org.springframework.http.ResponseEntity
|
|
||||||
import org.springframework.web.bind.annotation.RestController
|
|
||||||
import java.net.URL
|
|
||||||
|
|
||||||
@RestController
|
|
||||||
class InboxControllerImpl(
|
|
||||||
private val apService: APService,
|
|
||||||
private val httpSignatureHeaderChecker: HttpSignatureHeaderChecker,
|
|
||||||
) : InboxController {
|
|
||||||
@Suppress("TooGenericExceptionCaught")
|
|
||||||
override suspend fun inbox(
|
|
||||||
httpServletRequest: HttpServletRequest,
|
|
||||||
): ResponseEntity<String> {
|
|
||||||
val headersList = httpServletRequest.headerNames?.toList().orEmpty()
|
|
||||||
LOGGER.trace("Inbox Headers {}", headersList)
|
|
||||||
|
|
||||||
val body = withContext(Dispatchers.IO + MDCContext()) {
|
|
||||||
httpServletRequest.inputStream.readAllBytes()!!
|
|
||||||
}
|
|
||||||
|
|
||||||
val responseEntity = checkHeader(httpServletRequest, body)
|
|
||||||
|
|
||||||
if (responseEntity != null) {
|
|
||||||
return responseEntity
|
|
||||||
}
|
|
||||||
|
|
||||||
val parseActivity = try {
|
|
||||||
apService.parseActivity(body.decodeToString())
|
|
||||||
} catch (e: Exception) {
|
|
||||||
LOGGER.warn("FAILED Parse Activity", e)
|
|
||||||
return ResponseEntity.accepted().build()
|
|
||||||
}
|
|
||||||
LOGGER.info("INBOX Processing Activity Type: {}", parseActivity)
|
|
||||||
try {
|
|
||||||
val url = httpServletRequest.requestURL.toString()
|
|
||||||
|
|
||||||
val headers =
|
|
||||||
headersList.associateWith { header ->
|
|
||||||
httpServletRequest.getHeaders(header)?.toList().orEmpty()
|
|
||||||
}
|
|
||||||
|
|
||||||
apService.processActivity(
|
|
||||||
body.decodeToString(),
|
|
||||||
parseActivity,
|
|
||||||
HttpRequest(
|
|
||||||
URL(url + httpServletRequest.queryString.orEmpty()),
|
|
||||||
HttpHeaders(headers),
|
|
||||||
HttpMethod.POST
|
|
||||||
),
|
|
||||||
headers
|
|
||||||
)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
LOGGER.warn("FAILED Process Activity $parseActivity", e)
|
|
||||||
return ResponseEntity(HttpStatus.ACCEPTED)
|
|
||||||
}
|
|
||||||
LOGGER.info("SUCCESS Processing Activity Type: {}", parseActivity)
|
|
||||||
return ResponseEntity(HttpStatus.ACCEPTED)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun checkHeader(
|
|
||||||
httpServletRequest: HttpServletRequest,
|
|
||||||
body: ByteArray,
|
|
||||||
): ResponseEntity<String>? {
|
|
||||||
try {
|
|
||||||
httpSignatureHeaderChecker.checkDate(httpServletRequest.getHeader("date")!!)
|
|
||||||
} catch (_: NullPointerException) {
|
|
||||||
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("Required date header")
|
|
||||||
} catch (_: IllegalArgumentException) {
|
|
||||||
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Request is too old.")
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
httpSignatureHeaderChecker.checkHost(httpServletRequest.getHeader("host")!!)
|
|
||||||
} catch (_: NullPointerException) {
|
|
||||||
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("Required host header")
|
|
||||||
} catch (_: IllegalArgumentException) {
|
|
||||||
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Wrong host for request")
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
httpSignatureHeaderChecker.checkDigest(body, httpServletRequest.getHeader("digest")!!)
|
|
||||||
} catch (_: NullPointerException) {
|
|
||||||
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
|
|
||||||
.body("Required request body digest in digest header (sha256)")
|
|
||||||
} catch (_: IllegalArgumentException) {
|
|
||||||
return ResponseEntity
|
|
||||||
.status(HttpStatus.UNAUTHORIZED)
|
|
||||||
.body("Wrong digest for request")
|
|
||||||
}
|
|
||||||
|
|
||||||
if (httpServletRequest.getHeader("signature").orEmpty().isBlank()) {
|
|
||||||
return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
|
|
||||||
.header(
|
|
||||||
WWW_AUTHENTICATE,
|
|
||||||
"Signature realm=\"Example\",headers=\"(request-target) date host digest\""
|
|
||||||
)
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private val LOGGER = LoggerFactory.getLogger(InboxControllerImpl::class.java)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,82 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.activitypub.interfaces.api.nodeinfo
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.activitypub.domain.model.nodeinfo.Nodeinfo
|
|
||||||
import dev.usbharu.hideout.activitypub.domain.model.nodeinfo.Nodeinfo2_0
|
|
||||||
import dev.usbharu.hideout.application.config.ApplicationConfig
|
|
||||||
import org.springframework.http.HttpStatus
|
|
||||||
import org.springframework.http.ResponseEntity
|
|
||||||
import org.springframework.web.bind.annotation.GetMapping
|
|
||||||
import org.springframework.web.bind.annotation.RestController
|
|
||||||
|
|
||||||
@RestController
|
|
||||||
class NodeinfoController(private val applicationConfig: ApplicationConfig) {
|
|
||||||
@GetMapping("/.well-known/nodeinfo")
|
|
||||||
fun nodeinfo(): ResponseEntity<Nodeinfo> {
|
|
||||||
return ResponseEntity(
|
|
||||||
Nodeinfo(
|
|
||||||
listOf(
|
|
||||||
Nodeinfo.Links(
|
|
||||||
"http://nodeinfo.diaspora.software/ns/schema/2.0",
|
|
||||||
"${applicationConfig.url}/nodeinfo/2.0"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
HttpStatus.OK
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping("/nodeinfo/2.0")
|
|
||||||
@Suppress("FunctionNaming")
|
|
||||||
fun nodeinfo2_0(): ResponseEntity<Nodeinfo2_0> {
|
|
||||||
return ResponseEntity(
|
|
||||||
Nodeinfo2_0(
|
|
||||||
version = "2.0",
|
|
||||||
software = Nodeinfo2_0.Software(
|
|
||||||
name = "hideout",
|
|
||||||
version = "0.0.1"
|
|
||||||
),
|
|
||||||
protocols = listOf("activitypub"),
|
|
||||||
services = Nodeinfo2_0.Services(
|
|
||||||
inbound = emptyList(),
|
|
||||||
outbound = emptyList()
|
|
||||||
),
|
|
||||||
openRegistrations = false,
|
|
||||||
usage = Nodeinfo2_0.Usage(
|
|
||||||
users = Nodeinfo2_0.Usage.Users(
|
|
||||||
total = 1,
|
|
||||||
activeHalfYear = 1,
|
|
||||||
activeMonth = 1
|
|
||||||
),
|
|
||||||
localPosts = 1,
|
|
||||||
localComments = 0
|
|
||||||
),
|
|
||||||
metadata = Nodeinfo2_0.Metadata(
|
|
||||||
nodeName = "hideout",
|
|
||||||
nodeDescription = "hideout test server",
|
|
||||||
maintainer = Nodeinfo2_0.Metadata.Maintainer("usbharu", "i@usbharu.dev"),
|
|
||||||
langs = emptyList(),
|
|
||||||
tosUrl = "",
|
|
||||||
repositoryUrl = "https://github.com/usbharu/Hideout",
|
|
||||||
feedbackUrl = "https://github.com/usbharu/Hideout/issues/new/choose",
|
|
||||||
)
|
|
||||||
),
|
|
||||||
HttpStatus.OK
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue