Skip to content

Commit 1a5deed

Browse files
authored
#121 Add SOAP to REST Matchmaker (#124)
Signed-off-by: vityaman <vityaman.dev@yandex.ru>
1 parent 5ad8353 commit 1a5deed

16 files changed

+652
-7
lines changed

backend/buildSrc/src/main/kotlin/buildlogic.spring-conventions.gradle.kts

+6
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,9 @@ plugins {
44
id("io.spring.dependency-management")
55
kotlin("plugin.spring")
66
}
7+
8+
kotlin {
9+
compilerOptions {
10+
freeCompilerArgs.addAll("-Xjsr305=strict")
11+
}
12+
}

backend/gradle/libs.versions.toml

+8
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,15 @@ io-github-numichi-reactive-logger = "6.0.3"
3131
io-projectreactor-reactor-test = "3.6.11"
3232
junit-junit = "4.13.2"
3333
org-testcontainers = "1.20.3"
34+
org-junit-platform = "1.11.4"
35+
org-jetbrains-kotlin = "2.1.0"
3436

3537
[libraries]
3638
org-springframework-boot-spring-boot = { module = "org.springframework.boot:spring-boot", version.ref = "org-springframework-boot-spring-boot" }
3739
org-springframework-boot-spring-boot-starter-webflux = { module = "org.springframework.boot:spring-boot-starter-webflux", version.ref = "org-springframework-boot-spring-boot" }
3840
org-springframework-boot-spring-boot-starter-data-r2dbc = { module = "org.springframework.boot:spring-boot-starter-data-r2dbc", version.ref = "org-springframework-boot-spring-boot" }
3941
org-springframework-boot-spring-boot-starter-web = { module = "org.springframework.boot:spring-boot-starter-web", version.ref = "org-springframework-boot-spring-boot" }
42+
org-springframework-boot-spring-boot-starter-web-services = { module = "org.springframework.boot:spring-boot-starter-web-services", version.ref = "org-springframework-boot-spring-boot" }
4043
org-springframework-boot-spring-boot-starter-security = { module = "org.springframework.boot:spring-boot-starter-security", version.ref = "org-springframework-boot-spring-boot" }
4144
org-springframework-boot-spring-boot-starter-actuator = { module = "org.springframework.boot:spring-boot-starter-actuator", version.ref = "org-springframework-boot-spring-boot" }
4245
org-springframework-boot-spring-boot-starter-test = { module = "org.springframework.boot:spring-boot-starter-test", version.ref = "org-springframework-boot-spring-boot" }
@@ -84,10 +87,15 @@ io-micrometer-micrometer-registry-prometheus = { module = "io.micrometer:microme
8487
com-github-loki4j-loki-logback-appender = { module = "com.github.loki4j:loki-logback-appender", version.ref = "com-github-loki4j" }
8588
io-github-numichi-reactive-logger = { module = "io.github.numichi:reactive-logger", version.ref = "io-github-numichi-reactive-logger" }
8689

90+
org-jetbrains-kotlin-kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "org-jetbrains-kotlin" }
91+
8792
junit-junit = { module = "junit:junit", version.ref = "junit-junit" }
8893
org-testcontainers-postgresql = { module = "org.testcontainers:postgresql", version.ref = "org-testcontainers" }
8994
org-testcontainers-r2dbc = { module = "org.testcontainers:r2dbc", version.ref = "org-testcontainers" }
9095
org-testcontainers-minio = { module = "org.testcontainers:minio", version.ref = "org-testcontainers" }
9196
org-testcontainers-vault = { module = "org.testcontainers:vault", version.ref = "org-testcontainers" }
9297
org-testcontainers-junit-jupiter = { module = "org.testcontainers:junit-jupiter", version.ref = "org-testcontainers" }
9398
io-projectreactor-reactor-test = { module = "io.projectreactor:reactor-test", version.ref = "io-projectreactor-reactor-test" }
99+
org-jetbrains-kotlin-kotlin-test-junit5 = { module = "org.jetbrains.kotlin:kotlin-test-junit5", version.ref = "org-jetbrains-kotlin" }
100+
org-junit-platform-junit-platform-launcher = { module = "org.junit.platform:junit-platform-launcher", version.ref = "org-junit-platform" }
101+

backend/haproxy/config/haproxy.cfg

+11-6
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,15 @@ frontend internal
2828
bind :8455 ssl crt /usr/local/etc/haproxy/itmo-dating-backend.pem
2929
bind :8456 ssl crt /usr/local/etc/haproxy/itmo-dating-backend.pem
3030
bind :8457 ssl crt /usr/local/etc/haproxy/itmo-dating-backend.pem
31+
bind :8458 ssl crt /usr/local/etc/haproxy/itmo-dating-backend.pem
3132

32-
use_backend vault if { dst_port 8445 }
33-
use_backend consul if { dst_port 8446 }
34-
use_backend grafana if { dst_port 8447 }
35-
use_backend authik if { dst_port 8455 }
36-
use_backend matchmaker if { dst_port 8456 }
37-
use_backend people if { dst_port 8457 }
33+
use_backend vault if { dst_port 8445 }
34+
use_backend consul if { dst_port 8446 }
35+
use_backend grafana if { dst_port 8447 }
36+
use_backend authik if { dst_port 8455 }
37+
use_backend matchmaker if { dst_port 8456 }
38+
use_backend people if { dst_port 8457 }
39+
use_backend matchmaker-soap if { dst_port 8458 }
3840

3941
backend vault
4042
option httpchk GET /v1/sys/health?standbycode=200&sealedcode=200&uninitcode=200&drsecondarycode=200&performancestandbycode=200
@@ -64,3 +66,6 @@ backend people
6466
option httpchk GET /actuator/health
6567
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
6668
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
69+
70+
backend matchmaker-soap
71+
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

backend/matchmaker-soap/Dockerfile

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
FROM eclipse-temurin:23-jdk-alpine
2+
3+
WORKDIR /matchmaker-soap
4+
5+
COPY ./build/libs/matchmaker-soap-1.0.0.jar ./matchmaker-soap.jar
6+
7+
EXPOSE 8080
8+
9+
CMD ["java", "-jar", "matchmaker-soap.jar"]
+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import org.openapitools.generator.gradle.plugin.tasks.GenerateTask as OpenAPIGenerateTask
2+
3+
plugins {
4+
id("buildlogic.spring-conventions")
5+
id("com.github.bjornvester.wsdl2java") version "2.0.2"
6+
}
7+
8+
val projectGroup = group
9+
10+
val matchmaker = "matchmaker"
11+
val matchmakerTitle = matchmaker.replaceFirstChar { it.titlecase() }
12+
13+
val clientGeneratedDir = layout.buildDirectory
14+
.dir("generated/client/$matchmaker").get().toString()
15+
16+
tasks.register<OpenAPIGenerateTask>("openApiGenerate${matchmakerTitle}Client") {
17+
generatorName = "kotlin"
18+
inputSpec = rootProject.layout.projectDirectory.asFile
19+
.let { "$it/$matchmaker/src/main/resources/static/openapi/api.yml" }
20+
outputDir = clientGeneratedDir
21+
packageName = "$projectGroup.$matchmaker.client.generated"
22+
modelPackage = "$projectGroup.$matchmaker.client.model.generated"
23+
modelNameSuffix = "Message"
24+
configOptions = mapOf(
25+
"library" to "jvm-spring-restclient",
26+
"useSpringBoot3" to "true",
27+
"serializationLibrary" to "jackson",
28+
)
29+
}
30+
31+
val versionCxf = "4.1.0"
32+
33+
wsdl2java {
34+
wsdlDir = file("$projectDir/src/main/resources/wsdl")
35+
markGenerated = true
36+
cxfVersion = versionCxf
37+
}
38+
39+
sourceSets {
40+
main {
41+
kotlin {
42+
srcDir("$clientGeneratedDir/src/main/kotlin")
43+
}
44+
}
45+
}
46+
47+
tasks.compileKotlin.configure {
48+
dependsOn("openApiGenerate${matchmakerTitle}Client")
49+
dependsOn(tasks.wsdl2java)
50+
}
51+
52+
dependencies {
53+
api(project(":starter-tls"))
54+
55+
implementation(libs.org.springframework.boot.spring.boot.starter.web)
56+
implementation(libs.org.springframework.boot.spring.boot.starter.web.services)
57+
58+
implementation(libs.com.fasterxml.jackson.module.jackson.module.kotlin)
59+
implementation(libs.org.jetbrains.kotlin.kotlin.reflect)
60+
61+
implementation("org.springframework.ws:spring-ws-core:4.0.11")
62+
implementation("org.apache.cxf:cxf-spring-boot-starter-jaxws:$versionCxf")
63+
64+
testImplementation(libs.org.springframework.boot.spring.boot.starter.test)
65+
testImplementation(libs.org.jetbrains.kotlin.kotlin.reflect)
66+
testImplementation(libs.org.jetbrains.kotlin.kotlin.test.junit5)
67+
testRuntimeOnly(libs.org.junit.platform.junit.platform.launcher)
68+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package ru.ifmo.se.dating.matchmaker.soap
2+
3+
import org.springframework.boot.autoconfigure.SpringBootApplication
4+
import org.springframework.boot.runApplication
5+
import org.springframework.context.annotation.ComponentScan
6+
7+
@SpringBootApplication
8+
@ComponentScan(basePackages = ["ru.ifmo.se.dating"])
9+
class Application
10+
11+
fun main(args: Array<String>) {
12+
runApplication<Application>(args = args)
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package ru.ifmo.se.dating.matchmaker.soap
2+
3+
import ru.ifmo.se.dating.matchmaker.*
4+
import ru.ifmo.se.dating.matchmaker.client.model.generated.AttitudeKindMessage
5+
import ru.ifmo.se.dating.matchmaker.client.model.generated.PersonStatusMessage
6+
import ru.ifmo.se.dating.matchmaker.client.model.generated.PersonUpdateMessage
7+
import ru.ifmo.se.dating.matchmaker.client.model.generated.StatisticsAttitudesGet200ResponseInnerMessage
8+
9+
fun List<Long>.toSuggestionsSoap(): GetSuggestionsResponse =
10+
GetSuggestionsResponse().apply {
11+
personId.addAll(this@toSuggestionsSoap.map { it.toInt() })
12+
}
13+
14+
fun AttitudeKind.toRest(): AttitudeKindMessage =
15+
when (this) {
16+
AttitudeKind.LIKE -> AttitudeKindMessage.like
17+
AttitudeKind.SKIP -> AttitudeKindMessage.skip
18+
}
19+
20+
fun List<StatisticsAttitudesGet200ResponseInnerMessage>.toSoap(): GetAttitudesStatisticsResponse =
21+
GetAttitudesStatisticsResponse().apply {
22+
statistics.addAll(this@toSoap.map { it.toSoap() })
23+
}
24+
25+
fun StatisticsAttitudesGet200ResponseInnerMessage.toSoap() =
26+
GetAttitudesStatisticsResponse.Statistics().apply {
27+
personId = this@toSoap.personId.toInt()
28+
likes = this@toSoap.likes
29+
skips = this@toSoap.skips
30+
}
31+
32+
fun List<Long>.toMatchesSoap(): GetMatchesResponse =
33+
GetMatchesResponse().apply {
34+
personId.addAll(this@toMatchesSoap.map { it.toInt() })
35+
}
36+
37+
fun PersonUpdate.toRest(): PersonUpdateMessage =
38+
PersonUpdateMessage(
39+
status = this.status.toRest(),
40+
version = this.version,
41+
)
42+
43+
fun PersonStatus.toRest(): PersonStatusMessage =
44+
when (this) {
45+
PersonStatus.HIDDEN -> PersonStatusMessage.hidden
46+
PersonStatus.ACTIVE -> PersonStatusMessage.active
47+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package ru.ifmo.se.dating.matchmaker.soap
2+
3+
import org.springframework.web.client.RestClient
4+
import ru.ifmo.se.dating.matchmaker.client.generated.apis.PeopleApi
5+
import ru.ifmo.se.dating.matchmaker.client.generated.apis.StatisticsApi
6+
import ru.ifmo.se.dating.matchmaker.client.generated.apis.SuggestionsApi
7+
8+
class MatchmakerRestClient(private val client: RestClient) {
9+
fun people(authorization: String? = null) =
10+
PeopleApi(clientWithHeaders(authorization))
11+
12+
fun statistics(authorization: String? = null) =
13+
StatisticsApi(clientWithHeaders(authorization))
14+
15+
fun suggestions(authorization: String? = null) =
16+
SuggestionsApi(clientWithHeaders(authorization))
17+
18+
private fun clientWithHeaders(authorization: String? = null) =
19+
client.mutate()
20+
.apply { client ->
21+
authorization?.let {
22+
client.defaultHeader("Authorization", "Bearer $it")
23+
}
24+
}
25+
.build()
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package ru.ifmo.se.dating.matchmaker.soap
2+
3+
import org.springframework.stereotype.Controller
4+
import ru.ifmo.se.dating.matchmaker.*
5+
6+
@Controller
7+
class MatchmakerSoapService(
8+
private val rest: MatchmakerRestClient,
9+
) : ITMODatingMatchmakerPortType {
10+
override fun getSuggestions(
11+
authorization: String,
12+
parameters: GetSuggestionsRequest,
13+
): GetSuggestionsResponse =
14+
rest.suggestions(authorization = authorization)
15+
.suggestionsGet(limit = parameters.limit.toLong())
16+
.toSuggestionsSoap()
17+
18+
override fun likeSkip(
19+
authorization: String,
20+
parameters: LikeSkipRequest,
21+
): LikeSkipResponse =
22+
rest.suggestions(authorization = authorization)
23+
.peoplePersonIdAttitudesIncomingAttitudeKindPost(
24+
personId = parameters.personId.toLong(),
25+
attitudeKind = parameters.attitudeKind.toRest(),
26+
)
27+
.let { LikeSkipResponse() }
28+
29+
override fun getAttitudesStatistics(parameters: Any): GetAttitudesStatisticsResponse =
30+
rest.statistics()
31+
.statisticsAttitudesGet()
32+
.toSoap()
33+
34+
override fun getMatches(
35+
authorization: String,
36+
parameters: GetMatchesRequest,
37+
): GetMatchesResponse =
38+
rest.suggestions(authorization)
39+
.peoplePersonIdMatchesGet(personId = parameters.personId.toLong())
40+
.toMatchesSoap()
41+
42+
override fun updatePerson(parameters: UpdatePersonRequest): UpdatePersonResponse =
43+
rest.people()
44+
.peoplePersonIdPut(
45+
personId = parameters.personId.toLong(),
46+
personUpdateMessage = parameters.personUpdate.toRest(),
47+
)
48+
.let { UpdatePersonResponse() }
49+
50+
override fun resetAttitudes(
51+
authorization: String,
52+
parameters: ResetAttitudesRequest,
53+
): ResetAttitudesResponse =
54+
rest.suggestions(authorization)
55+
.attitudesDelete(sourceId = parameters.sourceId.toLong())
56+
.let { ResetAttitudesResponse() }
57+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package ru.ifmo.se.dating.matchmaker.soap
2+
3+
import org.springframework.beans.factory.annotation.Value
4+
import org.springframework.boot.autoconfigure.web.client.RestClientSsl
5+
import org.springframework.context.annotation.Bean
6+
import org.springframework.context.annotation.Configuration
7+
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter
8+
import org.springframework.web.client.RestClient
9+
10+
@Configuration
11+
class RestClientConfiguration {
12+
@Bean
13+
fun matchmakerRestClient(
14+
@Value("\${itmo-dating.matchmaker.url}")
15+
baseUrl: String,
16+
17+
ssl: RestClientSsl,
18+
): MatchmakerRestClient = RestClient.builder()
19+
.baseUrl("$baseUrl/api")
20+
.messageConverters { it.add(MappingJackson2HttpMessageConverter()) }
21+
.apply(ssl.fromBundle("internal"))
22+
.build()
23+
.let { MatchmakerRestClient(it) }
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package ru.ifmo.se.dating.matchmaker.soap
2+
3+
import jakarta.xml.ws.Endpoint
4+
import org.apache.cxf.Bus
5+
import org.apache.cxf.bus.spring.SpringBus
6+
import org.apache.cxf.jaxws.EndpointImpl
7+
import org.springframework.context.annotation.Bean
8+
import org.springframework.context.annotation.Configuration
9+
import ru.ifmo.se.dating.matchmaker.ITMODatingMatchmakerPortType
10+
11+
@Configuration
12+
class WebServiceConfiguration {
13+
@Bean(Bus.DEFAULT_BUS_ID)
14+
fun springBus(): SpringBus =
15+
SpringBus()
16+
17+
@Bean
18+
fun matchmakerEndpoint(
19+
bus: Bus,
20+
service: ITMODatingMatchmakerPortType,
21+
): Endpoint {
22+
val endpoint = EndpointImpl(bus, service)
23+
endpoint.publish("/matchmaker")
24+
return endpoint
25+
}
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
spring:
2+
config:
3+
import: application-tls.yml
4+
application:
5+
name: matchmaker-soap
6+
ssl:
7+
bundle:
8+
jks:
9+
internal:
10+
keystore:
11+
type: PKCS12
12+
location: classpath:keystore/itmo-dating-backend.p12
13+
password: ${ITMO_DATING_KEY_STORE_PASSWORD}
14+
truststore:
15+
type: PKCS12
16+
location: classpath:keystore/itmo-dating-backend.p12
17+
password: ${ITMO_DATING_KEY_STORE_PASSWORD}
18+
itmo-dating:
19+
matchmaker:
20+
url: https://matchmaker-0.dating.se.ifmo.ru:8080

0 commit comments

Comments
 (0)