Skip to content

Commit

Permalink
Pass safe and idempotent options to MethodDescriptor (#1774)
Browse files Browse the repository at this point in the history
* Pass `safe` and `idempotent` options to the MethodDescriptor

* Imply idempotent when safe is set. Emit options only when idempotency option is present. Add tests
  • Loading branch information
igor-vovk authored Feb 14, 2025
1 parent 82cf000 commit 261376f
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package scalapb.compiler

import com.google.protobuf.DescriptorProtos.MethodOptions.IdempotencyLevel
import com.google.protobuf.Descriptors.{MethodDescriptor, ServiceDescriptor}
import scalapb.compiler.FunctionalPrinter.PrinterEndo
import scalapb.compiler.ProtobufGenerator.asScalaDocBlock
Expand Down Expand Up @@ -177,6 +178,10 @@ final class GrpcServicePrinter(service: ServiceDescriptor, implicits: Descriptor
case StreamType.Bidirectional => "BIDI_STREAMING"
}

val idempotencyLevel = method.getOptions.getIdempotencyLevel
val safe = idempotencyLevel == IdempotencyLevel.NO_SIDE_EFFECTS
val idempotent = idempotencyLevel == IdempotencyLevel.IDEMPOTENT || safe

val grpcMethodDescriptor = "_root_.io.grpc.MethodDescriptor"

p.add(
Expand All @@ -188,9 +193,18 @@ final class GrpcServicePrinter(service: ServiceDescriptor, implicits: Descriptor
| .setRequestMarshaller(${marshaller(method.inputType)})
| .setResponseMarshaller(${marshaller(method.outputType)})
| .setSchemaDescriptor(_root_.scalapb.grpc.ConcreteProtoMethodDescriptorSupplier.fromMethodDescriptor(${method.javaDescriptorSource}))
| .build()
|""".stripMargin
)
|""".stripMargin.stripSuffix("\n")
).indented(
_.indented(
_.when(safe) {
_.add(".setSafe(true)")
}
.when(idempotent) {
_.add(".setIdempotent(true)")
}
.add(".build()")
)
).newline
}

private[this] def serviceDescriptor(service: ServiceDescriptor) = {
Expand Down
4 changes: 4 additions & 0 deletions e2e-grpc/src/main/protobuf/service.proto
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,10 @@ service Service1 {
rpc PrimitiveValues(google.protobuf.Int32Value) returns (google.protobuf.StringValue) {};

rpc EchoRequest(Req1) returns (Res6) {}

rpc NoSideEffects(Req1) returns (Res6) {
option idempotency_level = NO_SIDE_EFFECTS;
}
}

service Issue774 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,4 +126,8 @@ class Service1ScalaImpl extends Service1 {
}

override def primitiveValues(request: Int): Future[String] = Future.successful("boo")

override def noSideEffects(request: Req1): Future[Res6] =
Future.successful(Res6(Some(request)))

}
10 changes: 9 additions & 1 deletion e2e-grpc/src/test/scala/MethodDescriptorSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,17 @@ import org.scalatest.{LoneElement, OptionValues}
class MethodDescriptorSpec extends AnyFlatSpec with Matchers with LoneElement with OptionValues {

"scala descriptor" must "expose correct input and output message descriptors" in {
val unaryMethod = Service1Grpc.Service1.scalaDescriptor.methods.find(_.name == "CustomUnary").get
val unaryMethod =
Service1Grpc.Service1.scalaDescriptor.methods.find(_.name == "CustomUnary").get

unaryMethod.inputType must be theSameInstanceAs XYMessage.scalaDescriptor
unaryMethod.outputType must be theSameInstanceAs Res5.scalaDescriptor
}

"java descriptor" must "set idempotent and safe options when idempotency_level is set" in {
val noSideEffMethod = Service1Grpc.METHOD_NO_SIDE_EFFECTS

noSideEffMethod.isSafe must be(true)
noSideEffMethod.isIdempotent must be(true)
}
}

0 comments on commit 261376f

Please sign in to comment.