Skip to content

Commit 7f99268

Browse files
committed
Change builder API
1 parent fb1a8c1 commit 7f99268

File tree

4 files changed

+70
-48
lines changed

4 files changed

+70
-48
lines changed

conformance/src/main/scala/org/ivovk/connect_rpc_scala/conformance/Main.scala

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import com.comcast.ip4s.{Port, host, port}
55
import connectrpc.conformance.v1.{ConformanceServiceFs2GrpcTrailers, ServerCompatRequest, ServerCompatResponse}
66
import org.http4s.ember.server.EmberServerBuilder
77
import org.ivovk.connect_rpc_scala.ConnectRouteBuilder
8-
import org.ivovk.connect_rpc_scala.http.codec.JsonMessageCodecBuilder
98

109
import java.io.InputStream
1110
import java.nio.ByteBuffer
@@ -36,19 +35,14 @@ object Main extends IOApp.Simple {
3635
ConformanceServiceImpl[IO]()
3736
)
3837

39-
jsonCodec = JsonMessageCodecBuilder[IO]()
40-
// Registering message types in TypeRegistry is required to pass com.google.protobuf.any.Any
41-
// JSON-serialization conformance tests
42-
.withTypeRegistryConfigurator { tp =>
43-
tp
44-
.addMessage[connectrpc.conformance.v1.UnaryRequest]
45-
.addMessage[connectrpc.conformance.v1.IdempotentUnaryRequest]
46-
.addMessage[connectrpc.conformance.v1.ConformancePayload.RequestInfo]
47-
}
48-
.build
49-
5038
app <- ConnectRouteBuilder.forService[IO](service)
51-
.withJsonCodec(jsonCodec)
39+
.withJsonCodecConfigurator {
40+
// Registering message types in TypeRegistry is required to pass com.google.protobuf.any.Any
41+
// JSON-serialization conformance tests
42+
_
43+
.registerType[connectrpc.conformance.v1.UnaryRequest]
44+
.registerType[connectrpc.conformance.v1.IdempotentUnaryRequest]
45+
}
5246
.build
5347

5448
server <- EmberServerBuilder.default[IO]

core/src/main/scala/org/ivovk/connect_rpc_scala/ConnectRouteBuilder.scala

Lines changed: 48 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -18,38 +18,66 @@ import scala.concurrent.duration.*
1818
object ConnectRouteBuilder {
1919

2020
def forService[F[_] : Async](service: ServerServiceDefinition): ConnectRouteBuilder[F] =
21-
ConnectRouteBuilder(Seq(service))
21+
forServices(Seq(service))
2222

2323
def forServices[F[_] : Async](service: ServerServiceDefinition, other: ServerServiceDefinition*): ConnectRouteBuilder[F] =
24-
ConnectRouteBuilder(service +: other)
24+
forServices(service +: other)
2525

2626
def forServices[F[_] : Async](services: Seq[ServerServiceDefinition]): ConnectRouteBuilder[F] =
27-
ConnectRouteBuilder(services)
27+
new ConnectRouteBuilder(
28+
services = services,
29+
serverConfigurator = identity,
30+
channelConfigurator = identity,
31+
customJsonCodec = None,
32+
pathPrefix = Uri.Path.Root,
33+
executor = ExecutionContext.global,
34+
waitForShutdown = 5.seconds,
35+
treatTrailersAsHeaders = true,
36+
)
2837

2938
}
3039

31-
case class ConnectRouteBuilder[F[_] : Async] private(
40+
final class ConnectRouteBuilder[F[_] : Async] private(
3241
services: Seq[ServerServiceDefinition],
33-
serverConfigurator: Endo[ServerBuilder[_]] = identity,
34-
channelConfigurator: Endo[ManagedChannelBuilder[_]] = identity,
35-
customJsonCodec: Option[JsonMessageCodec[F]] = None,
36-
pathPrefix: Uri.Path = Uri.Path.Root,
37-
executor: Executor = ExecutionContext.global,
38-
waitForShutdown: Duration = 5.seconds,
39-
treatTrailersAsHeaders: Boolean = true,
42+
serverConfigurator: Endo[ServerBuilder[_]],
43+
channelConfigurator: Endo[ManagedChannelBuilder[_]],
44+
customJsonCodec: Option[JsonMessageCodec[F]],
45+
pathPrefix: Uri.Path,
46+
executor: Executor,
47+
waitForShutdown: Duration,
48+
treatTrailersAsHeaders: Boolean,
4049
) {
4150

42-
import Mappings.*
43-
44-
def withJsonCodec(codec: JsonMessageCodec[F]): ConnectRouteBuilder[F] =
45-
copy(customJsonCodec = Some(codec))
51+
private def copy(
52+
services: Seq[ServerServiceDefinition] = services,
53+
serverConfigurator: Endo[ServerBuilder[_]] = serverConfigurator,
54+
channelConfigurator: Endo[ManagedChannelBuilder[_]] = channelConfigurator,
55+
customJsonCodec: Option[JsonMessageCodec[F]] = customJsonCodec,
56+
pathPrefix: Uri.Path = pathPrefix,
57+
executor: Executor = executor,
58+
waitForShutdown: Duration = waitForShutdown,
59+
treatTrailersAsHeaders: Boolean = treatTrailersAsHeaders,
60+
): ConnectRouteBuilder[F] =
61+
new ConnectRouteBuilder(
62+
services,
63+
serverConfigurator,
64+
channelConfigurator,
65+
customJsonCodec,
66+
pathPrefix,
67+
executor,
68+
waitForShutdown,
69+
treatTrailersAsHeaders,
70+
)
4671

4772
def withServerConfigurator(method: Endo[ServerBuilder[_]]): ConnectRouteBuilder[F] =
4873
copy(serverConfigurator = method)
4974

5075
def withChannelConfigurator(method: Endo[ManagedChannelBuilder[_]]): ConnectRouteBuilder[F] =
5176
copy(channelConfigurator = method)
5277

78+
def withJsonCodecConfigurator(method: Endo[JsonMessageCodecBuilder[F]]): ConnectRouteBuilder[F] =
79+
copy(customJsonCodec = Some(method(JsonMessageCodecBuilder[F]()).build))
80+
5381
def withPathPrefix(path: Uri.Path): ConnectRouteBuilder[F] =
5482
copy(pathPrefix = path)
5583

@@ -60,19 +88,20 @@ case class ConnectRouteBuilder[F[_] : Async] private(
6088
copy(waitForShutdown = duration)
6189

6290
/**
63-
* If enabled, trailers will be treated as headers (no "trailer-" prefix).
91+
* When enabled, response trailers are treated as headers (no "trailer-" prefix added).
6492
*
6593
* Both `fs2-grpc` and `zio-grpc` support trailing headers only, so enabling this option is a single way to
66-
* send headers from the server to the client.
94+
* send headers from the server to a client.
6795
*
6896
* Enabled by default.
6997
*/
7098
def withTreatTrailersAsHeaders(enabled: Boolean): ConnectRouteBuilder[F] =
7199
copy(treatTrailersAsHeaders = enabled)
72100

73101
/**
74-
* Method can be used if you want to add additional routes to the server.
75-
* Otherwise, it is preferred to use the [[build]] method.
102+
* Use this method only if you want to add additional routes to the server.
103+
*
104+
* Otherwise, [[build]] method should be preferred.
76105
*/
77106
def buildRoutes: Resource[F, HttpRoutes[F]] = {
78107
val httpDsl = Http4sDsl[F]

core/src/main/scala/org/ivovk/connect_rpc_scala/http/codec/JsonMessageCodecBuilder.scala

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,36 @@
11
package org.ivovk.connect_rpc_scala.http.codec
22

3-
import cats.Endo
43
import cats.effect.Sync
54
import org.ivovk.connect_rpc_scala.http.json.ErrorDetailsAnyFormat
6-
import scalapb.json4s
5+
import scalapb.json4s.{FormatRegistry, JsonFormat, TypeRegistry}
6+
import scalapb.{GeneratedMessage, GeneratedMessageCompanion, json4s}
77

88
import scala.util.chaining.*
99

1010
object JsonMessageCodecBuilder {
11-
def apply[F[_] : Sync](): JsonMessageCodecBuilder[F] = new JsonMessageCodecBuilder()
11+
def apply[F[_] : Sync](): JsonMessageCodecBuilder[F] =
12+
new JsonMessageCodecBuilder(
13+
typeRegistry = TypeRegistry.default,
14+
formatRegistry = JsonFormat.DefaultRegistry,
15+
)
1216
}
1317

1418
case class JsonMessageCodecBuilder[F[_] : Sync] private(
15-
typeRegistryConfigurator: Endo[json4s.TypeRegistry] = identity,
16-
formatRegistryConfigurator: Endo[json4s.FormatRegistry] = identity,
19+
typeRegistry: TypeRegistry,
20+
formatRegistry: FormatRegistry,
1721
) {
1822

19-
def withTypeRegistryConfigurator(method: Endo[json4s.TypeRegistry]): JsonMessageCodecBuilder[F] =
20-
copy(typeRegistryConfigurator = method)
21-
22-
def withFormatRegistryConfigurator(method: Endo[json4s.FormatRegistry]): JsonMessageCodecBuilder[F] =
23-
copy(formatRegistryConfigurator = method)
23+
def registerType[T <: GeneratedMessage](using cmp: GeneratedMessageCompanion[T]): JsonMessageCodecBuilder[F] =
24+
copy(
25+
typeRegistry = typeRegistry.addMessageByCompanion(cmp),
26+
)
2427

2528
def build: JsonMessageCodec[F] = {
26-
val typeRegistry = json4s.TypeRegistry.default
27-
.pipe(typeRegistryConfigurator)
28-
29-
val formatRegistry = json4s.JsonFormat.DefaultRegistry
29+
val formatRegistry = this.formatRegistry
3030
.registerMessageFormatter[connectrpc.ErrorDetailsAny](
3131
ErrorDetailsAnyFormat.writer,
3232
ErrorDetailsAnyFormat.printer
3333
)
34-
.pipe(formatRegistryConfigurator)
3534

3635
val parser = new json4s.Parser()
3736
.withTypeRegistry(typeRegistry)

core/src/test/scala/org/ivovk/connect_rpc_scala/http/json/JsonSerializationTest.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import scalapb.json4s
66

77
class JsonSerializationTest extends AnyFunSuite {
88

9-
test("ErrorDetailsAny serialization/deserialization") {
9+
test("ErrorDetailsAny serialization") {
1010
val formatRegistry = json4s.JsonFormat.DefaultRegistry
1111
.registerMessageFormatter[connectrpc.ErrorDetailsAny](
1212
ErrorDetailsAnyFormat.writer,

0 commit comments

Comments
 (0)