Skip to content

Commit

Permalink
#121 Add SOAP to REST Matchmaker (#124)
Browse files Browse the repository at this point in the history
Signed-off-by: vityaman <[email protected]>
  • Loading branch information
vityaman authored Jan 28, 2025
1 parent 5ad8353 commit 1a5deed
Show file tree
Hide file tree
Showing 16 changed files with 652 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,9 @@ plugins {
id("io.spring.dependency-management")
kotlin("plugin.spring")
}

kotlin {
compilerOptions {
freeCompilerArgs.addAll("-Xjsr305=strict")
}
}
8 changes: 8 additions & 0 deletions backend/gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,15 @@ io-github-numichi-reactive-logger = "6.0.3"
io-projectreactor-reactor-test = "3.6.11"
junit-junit = "4.13.2"
org-testcontainers = "1.20.3"
org-junit-platform = "1.11.4"
org-jetbrains-kotlin = "2.1.0"

[libraries]
org-springframework-boot-spring-boot = { module = "org.springframework.boot:spring-boot", version.ref = "org-springframework-boot-spring-boot" }
org-springframework-boot-spring-boot-starter-webflux = { module = "org.springframework.boot:spring-boot-starter-webflux", version.ref = "org-springframework-boot-spring-boot" }
org-springframework-boot-spring-boot-starter-data-r2dbc = { module = "org.springframework.boot:spring-boot-starter-data-r2dbc", version.ref = "org-springframework-boot-spring-boot" }
org-springframework-boot-spring-boot-starter-web = { module = "org.springframework.boot:spring-boot-starter-web", version.ref = "org-springframework-boot-spring-boot" }
org-springframework-boot-spring-boot-starter-web-services = { module = "org.springframework.boot:spring-boot-starter-web-services", version.ref = "org-springframework-boot-spring-boot" }
org-springframework-boot-spring-boot-starter-security = { module = "org.springframework.boot:spring-boot-starter-security", version.ref = "org-springframework-boot-spring-boot" }
org-springframework-boot-spring-boot-starter-actuator = { module = "org.springframework.boot:spring-boot-starter-actuator", version.ref = "org-springframework-boot-spring-boot" }
org-springframework-boot-spring-boot-starter-test = { module = "org.springframework.boot:spring-boot-starter-test", version.ref = "org-springframework-boot-spring-boot" }
Expand Down Expand Up @@ -84,10 +87,15 @@ io-micrometer-micrometer-registry-prometheus = { module = "io.micrometer:microme
com-github-loki4j-loki-logback-appender = { module = "com.github.loki4j:loki-logback-appender", version.ref = "com-github-loki4j" }
io-github-numichi-reactive-logger = { module = "io.github.numichi:reactive-logger", version.ref = "io-github-numichi-reactive-logger" }

org-jetbrains-kotlin-kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "org-jetbrains-kotlin" }

junit-junit = { module = "junit:junit", version.ref = "junit-junit" }
org-testcontainers-postgresql = { module = "org.testcontainers:postgresql", version.ref = "org-testcontainers" }
org-testcontainers-r2dbc = { module = "org.testcontainers:r2dbc", version.ref = "org-testcontainers" }
org-testcontainers-minio = { module = "org.testcontainers:minio", version.ref = "org-testcontainers" }
org-testcontainers-vault = { module = "org.testcontainers:vault", version.ref = "org-testcontainers" }
org-testcontainers-junit-jupiter = { module = "org.testcontainers:junit-jupiter", version.ref = "org-testcontainers" }
io-projectreactor-reactor-test = { module = "io.projectreactor:reactor-test", version.ref = "io-projectreactor-reactor-test" }
org-jetbrains-kotlin-kotlin-test-junit5 = { module = "org.jetbrains.kotlin:kotlin-test-junit5", version.ref = "org-jetbrains-kotlin" }
org-junit-platform-junit-platform-launcher = { module = "org.junit.platform:junit-platform-launcher", version.ref = "org-junit-platform" }

17 changes: 11 additions & 6 deletions backend/haproxy/config/haproxy.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,15 @@ frontend internal
bind :8455 ssl crt /usr/local/etc/haproxy/itmo-dating-backend.pem
bind :8456 ssl crt /usr/local/etc/haproxy/itmo-dating-backend.pem
bind :8457 ssl crt /usr/local/etc/haproxy/itmo-dating-backend.pem
bind :8458 ssl crt /usr/local/etc/haproxy/itmo-dating-backend.pem

use_backend vault if { dst_port 8445 }
use_backend consul if { dst_port 8446 }
use_backend grafana if { dst_port 8447 }
use_backend authik if { dst_port 8455 }
use_backend matchmaker if { dst_port 8456 }
use_backend people if { dst_port 8457 }
use_backend vault if { dst_port 8445 }
use_backend consul if { dst_port 8446 }
use_backend grafana if { dst_port 8447 }
use_backend authik if { dst_port 8455 }
use_backend matchmaker if { dst_port 8456 }
use_backend people if { dst_port 8457 }
use_backend matchmaker-soap if { dst_port 8458 }

backend vault
option httpchk GET /v1/sys/health?standbycode=200&sealedcode=200&uninitcode=200&drsecondarycode=200&performancestandbycode=200
Expand Down Expand Up @@ -64,3 +66,6 @@ backend people
option httpchk GET /actuator/health
server people-0 people-0.dating.se.ifmo.ru:8080 check init-addr last,libc,none ssl verify required ca-file /usr/local/etc/haproxy/itmo-dating-backend-ca.crt
server people-1 people-1.dating.se.ifmo.ru:8080 check init-addr last,libc,none ssl verify required ca-file /usr/local/etc/haproxy/itmo-dating-backend-ca.crt

backend matchmaker-soap
server matchmaker-soap matchmaker-soap.dating.se.ifmo.ru:8080 check init-addr last,libc,none ssl verify required ca-file /usr/local/etc/haproxy/itmo-dating-backend-ca.crt
9 changes: 9 additions & 0 deletions backend/matchmaker-soap/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
FROM eclipse-temurin:23-jdk-alpine

WORKDIR /matchmaker-soap

COPY ./build/libs/matchmaker-soap-1.0.0.jar ./matchmaker-soap.jar

EXPOSE 8080

CMD ["java", "-jar", "matchmaker-soap.jar"]
68 changes: 68 additions & 0 deletions backend/matchmaker-soap/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import org.openapitools.generator.gradle.plugin.tasks.GenerateTask as OpenAPIGenerateTask

plugins {
id("buildlogic.spring-conventions")
id("com.github.bjornvester.wsdl2java") version "2.0.2"
}

val projectGroup = group

val matchmaker = "matchmaker"
val matchmakerTitle = matchmaker.replaceFirstChar { it.titlecase() }

val clientGeneratedDir = layout.buildDirectory
.dir("generated/client/$matchmaker").get().toString()

tasks.register<OpenAPIGenerateTask>("openApiGenerate${matchmakerTitle}Client") {
generatorName = "kotlin"
inputSpec = rootProject.layout.projectDirectory.asFile
.let { "$it/$matchmaker/src/main/resources/static/openapi/api.yml" }
outputDir = clientGeneratedDir
packageName = "$projectGroup.$matchmaker.client.generated"
modelPackage = "$projectGroup.$matchmaker.client.model.generated"
modelNameSuffix = "Message"
configOptions = mapOf(
"library" to "jvm-spring-restclient",
"useSpringBoot3" to "true",
"serializationLibrary" to "jackson",
)
}

val versionCxf = "4.1.0"

wsdl2java {
wsdlDir = file("$projectDir/src/main/resources/wsdl")
markGenerated = true
cxfVersion = versionCxf
}

sourceSets {
main {
kotlin {
srcDir("$clientGeneratedDir/src/main/kotlin")
}
}
}

tasks.compileKotlin.configure {
dependsOn("openApiGenerate${matchmakerTitle}Client")
dependsOn(tasks.wsdl2java)
}

dependencies {
api(project(":starter-tls"))

implementation(libs.org.springframework.boot.spring.boot.starter.web)
implementation(libs.org.springframework.boot.spring.boot.starter.web.services)

implementation(libs.com.fasterxml.jackson.module.jackson.module.kotlin)
implementation(libs.org.jetbrains.kotlin.kotlin.reflect)

implementation("org.springframework.ws:spring-ws-core:4.0.11")
implementation("org.apache.cxf:cxf-spring-boot-starter-jaxws:$versionCxf")

testImplementation(libs.org.springframework.boot.spring.boot.starter.test)
testImplementation(libs.org.jetbrains.kotlin.kotlin.reflect)
testImplementation(libs.org.jetbrains.kotlin.kotlin.test.junit5)
testRuntimeOnly(libs.org.junit.platform.junit.platform.launcher)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package ru.ifmo.se.dating.matchmaker.soap

import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.context.annotation.ComponentScan

@SpringBootApplication
@ComponentScan(basePackages = ["ru.ifmo.se.dating"])
class Application

fun main(args: Array<String>) {
runApplication<Application>(args = args)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package ru.ifmo.se.dating.matchmaker.soap

import ru.ifmo.se.dating.matchmaker.*
import ru.ifmo.se.dating.matchmaker.client.model.generated.AttitudeKindMessage
import ru.ifmo.se.dating.matchmaker.client.model.generated.PersonStatusMessage
import ru.ifmo.se.dating.matchmaker.client.model.generated.PersonUpdateMessage
import ru.ifmo.se.dating.matchmaker.client.model.generated.StatisticsAttitudesGet200ResponseInnerMessage

fun List<Long>.toSuggestionsSoap(): GetSuggestionsResponse =
GetSuggestionsResponse().apply {
personId.addAll(this@toSuggestionsSoap.map { it.toInt() })
}

fun AttitudeKind.toRest(): AttitudeKindMessage =
when (this) {
AttitudeKind.LIKE -> AttitudeKindMessage.like
AttitudeKind.SKIP -> AttitudeKindMessage.skip
}

fun List<StatisticsAttitudesGet200ResponseInnerMessage>.toSoap(): GetAttitudesStatisticsResponse =
GetAttitudesStatisticsResponse().apply {
statistics.addAll(this@toSoap.map { it.toSoap() })
}

fun StatisticsAttitudesGet200ResponseInnerMessage.toSoap() =
GetAttitudesStatisticsResponse.Statistics().apply {
personId = this@toSoap.personId.toInt()
likes = this@toSoap.likes
skips = this@toSoap.skips
}

fun List<Long>.toMatchesSoap(): GetMatchesResponse =
GetMatchesResponse().apply {
personId.addAll(this@toMatchesSoap.map { it.toInt() })
}

fun PersonUpdate.toRest(): PersonUpdateMessage =
PersonUpdateMessage(
status = this.status.toRest(),
version = this.version,
)

fun PersonStatus.toRest(): PersonStatusMessage =
when (this) {
PersonStatus.HIDDEN -> PersonStatusMessage.hidden
PersonStatus.ACTIVE -> PersonStatusMessage.active
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package ru.ifmo.se.dating.matchmaker.soap

import org.springframework.web.client.RestClient
import ru.ifmo.se.dating.matchmaker.client.generated.apis.PeopleApi
import ru.ifmo.se.dating.matchmaker.client.generated.apis.StatisticsApi
import ru.ifmo.se.dating.matchmaker.client.generated.apis.SuggestionsApi

class MatchmakerRestClient(private val client: RestClient) {
fun people(authorization: String? = null) =
PeopleApi(clientWithHeaders(authorization))

fun statistics(authorization: String? = null) =
StatisticsApi(clientWithHeaders(authorization))

fun suggestions(authorization: String? = null) =
SuggestionsApi(clientWithHeaders(authorization))

private fun clientWithHeaders(authorization: String? = null) =
client.mutate()
.apply { client ->
authorization?.let {
client.defaultHeader("Authorization", "Bearer $it")
}
}
.build()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package ru.ifmo.se.dating.matchmaker.soap

import org.springframework.stereotype.Controller
import ru.ifmo.se.dating.matchmaker.*

@Controller
class MatchmakerSoapService(
private val rest: MatchmakerRestClient,
) : ITMODatingMatchmakerPortType {
override fun getSuggestions(
authorization: String,
parameters: GetSuggestionsRequest,
): GetSuggestionsResponse =
rest.suggestions(authorization = authorization)
.suggestionsGet(limit = parameters.limit.toLong())
.toSuggestionsSoap()

override fun likeSkip(
authorization: String,
parameters: LikeSkipRequest,
): LikeSkipResponse =
rest.suggestions(authorization = authorization)
.peoplePersonIdAttitudesIncomingAttitudeKindPost(
personId = parameters.personId.toLong(),
attitudeKind = parameters.attitudeKind.toRest(),
)
.let { LikeSkipResponse() }

override fun getAttitudesStatistics(parameters: Any): GetAttitudesStatisticsResponse =
rest.statistics()
.statisticsAttitudesGet()
.toSoap()

override fun getMatches(
authorization: String,
parameters: GetMatchesRequest,
): GetMatchesResponse =
rest.suggestions(authorization)
.peoplePersonIdMatchesGet(personId = parameters.personId.toLong())
.toMatchesSoap()

override fun updatePerson(parameters: UpdatePersonRequest): UpdatePersonResponse =
rest.people()
.peoplePersonIdPut(
personId = parameters.personId.toLong(),
personUpdateMessage = parameters.personUpdate.toRest(),
)
.let { UpdatePersonResponse() }

override fun resetAttitudes(
authorization: String,
parameters: ResetAttitudesRequest,
): ResetAttitudesResponse =
rest.suggestions(authorization)
.attitudesDelete(sourceId = parameters.sourceId.toLong())
.let { ResetAttitudesResponse() }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package ru.ifmo.se.dating.matchmaker.soap

import org.springframework.beans.factory.annotation.Value
import org.springframework.boot.autoconfigure.web.client.RestClientSsl
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter
import org.springframework.web.client.RestClient

@Configuration
class RestClientConfiguration {
@Bean
fun matchmakerRestClient(
@Value("\${itmo-dating.matchmaker.url}")
baseUrl: String,

ssl: RestClientSsl,
): MatchmakerRestClient = RestClient.builder()
.baseUrl("$baseUrl/api")
.messageConverters { it.add(MappingJackson2HttpMessageConverter()) }
.apply(ssl.fromBundle("internal"))
.build()
.let { MatchmakerRestClient(it) }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package ru.ifmo.se.dating.matchmaker.soap

import jakarta.xml.ws.Endpoint
import org.apache.cxf.Bus
import org.apache.cxf.bus.spring.SpringBus
import org.apache.cxf.jaxws.EndpointImpl
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import ru.ifmo.se.dating.matchmaker.ITMODatingMatchmakerPortType

@Configuration
class WebServiceConfiguration {
@Bean(Bus.DEFAULT_BUS_ID)
fun springBus(): SpringBus =
SpringBus()

@Bean
fun matchmakerEndpoint(
bus: Bus,
service: ITMODatingMatchmakerPortType,
): Endpoint {
val endpoint = EndpointImpl(bus, service)
endpoint.publish("/matchmaker")
return endpoint
}
}
20 changes: 20 additions & 0 deletions backend/matchmaker-soap/src/main/resources/application.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
spring:
config:
import: application-tls.yml
application:
name: matchmaker-soap
ssl:
bundle:
jks:
internal:
keystore:
type: PKCS12
location: classpath:keystore/itmo-dating-backend.p12
password: ${ITMO_DATING_KEY_STORE_PASSWORD}
truststore:
type: PKCS12
location: classpath:keystore/itmo-dating-backend.p12
password: ${ITMO_DATING_KEY_STORE_PASSWORD}
itmo-dating:
matchmaker:
url: https://matchmaker-0.dating.se.ifmo.ru:8080
Loading

0 comments on commit 1a5deed

Please sign in to comment.