From 0d71168505b9afe53f565da8cd7453313336f275 Mon Sep 17 00:00:00 2001 From: Ronald Holshausen Date: Thu, 8 Aug 2024 16:06:12 +1000 Subject: [PATCH] feat: Add gRPC exmaple using provider state injected values with metadata --- .../grpc/consumer/PactConsumerTest.java | 15 +++++++ .../io/pact/example/grpc/provider/Server.kt | 40 ++++++++++++++++++- .../grpc/provider/PactVerificationTest.java | 6 ++- 3 files changed, 59 insertions(+), 2 deletions(-) diff --git a/examples/gRPC/provider-states/consumer-jvm/src/test/java/io/pact/example/grpc/consumer/PactConsumerTest.java b/examples/gRPC/provider-states/consumer-jvm/src/test/java/io/pact/example/grpc/consumer/PactConsumerTest.java index 918248e4..f69789e2 100644 --- a/examples/gRPC/provider-states/consumer-jvm/src/test/java/io/pact/example/grpc/consumer/PactConsumerTest.java +++ b/examples/gRPC/provider-states/consumer-jvm/src/test/java/io/pact/example/grpc/consumer/PactConsumerTest.java @@ -16,14 +16,18 @@ import com.google.protobuf.InvalidProtocolBufferException; import io.grpc.ManagedChannel; import io.grpc.ManagedChannelBuilder; +import io.grpc.Metadata; +import io.grpc.stub.MetadataUtils; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import java.util.List; import java.util.Map; +import java.util.concurrent.atomic.AtomicReference; import static area_calculator.CalculatorGrpc.newBlockingStub; import static au.com.dius.pact.consumer.dsl.PactBuilder.filePath; +import static io.grpc.Metadata.ASCII_STRING_MARSHALLER; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; @@ -48,6 +52,10 @@ V4Pact calculateRectangleArea(PactBuilder builder) { "width", "matching(number, fromProviderState('${rectangleWidth}', 3))" )), + "requestMetadata", Map.of( + "Auth", "matching(equalTo, fromProviderState('${Auth}', 'AST00004'))" + ), + "response", List.of( Map.of( "value", "matching(number, 12)" @@ -61,8 +69,15 @@ V4Pact calculateRectangleArea(PactBuilder builder) { @PactTestFor(pactMethod = "calculateRectangleArea") @MockServerConfig(implementation = MockServerImplementation.Plugin, registryEntry = "protobuf/transport/grpc") void calculateRectangleArea(MockServer mockServer, V4Interaction.SynchronousMessages interaction) throws InvalidProtocolBufferException { + io.grpc.Metadata.Key authKey = io.grpc.Metadata.Key.of("Auth", ASCII_STRING_MARSHALLER); + io.grpc.Metadata metadata = new io.grpc.Metadata(); + metadata.put(authKey, "AST00004"); + AtomicReference headers = new AtomicReference<>(null); + AtomicReference trailers = new AtomicReference<>(null); ManagedChannel channel = ManagedChannelBuilder.forTarget("127.0.0.1:" + mockServer.getPort()) .usePlaintext() + .intercept(MetadataUtils.newAttachHeadersInterceptor(metadata)) + .intercept(MetadataUtils.newCaptureMetadataInterceptor(headers, trailers)) .build(); CalculatorGrpc.CalculatorBlockingStub stub = newBlockingStub(channel); diff --git a/examples/gRPC/provider-states/provider-jvm/server/src/main/kotlin/io/pact/example/grpc/provider/Server.kt b/examples/gRPC/provider-states/provider-jvm/server/src/main/kotlin/io/pact/example/grpc/provider/Server.kt index 34ca3731..33c3034a 100644 --- a/examples/gRPC/provider-states/provider-jvm/server/src/main/kotlin/io/pact/example/grpc/provider/Server.kt +++ b/examples/gRPC/provider-states/provider-jvm/server/src/main/kotlin/io/pact/example/grpc/provider/Server.kt @@ -2,14 +2,22 @@ package io.pact.example.grpc.provider import area_calculator.AreaCalculator import area_calculator.CalculatorGrpcKt +import io.grpc.Metadata import io.grpc.ServerBuilder +import io.grpc.ServerCall +import io.grpc.ServerCallHandler +import io.grpc.ServerInterceptor +import io.grpc.Status import mu.KLogging +import java.util.regex.Pattern import kotlin.math.PI import kotlin.math.pow import kotlin.math.sqrt class Server { - private val server = ServerBuilder.forPort(0).addService(CalculatorService()).build() + private val server = ServerBuilder.forPort(0) + .intercept(CalculatorInterceptor()) + .addService(CalculatorService()).build() fun start() { server.start() @@ -34,6 +42,36 @@ class Server { fun serverPort() = server.port } +class CalculatorInterceptor : ServerInterceptor { + val authKey: Metadata.Key = Metadata.Key.of("Auth", Metadata.ASCII_STRING_MARSHALLER) + val authCheckPatten: Pattern = Pattern.compile("[A-Z]{3}\\d{3}") + + override fun interceptCall( + call: ServerCall, + headers: Metadata?, + next: ServerCallHandler? + ): ServerCall.Listener { + val auth = headers?.get(authKey) + return if (auth.isNullOrEmpty()) { + call.close(Status.UNAUTHENTICATED, Metadata()) + NoOpListener() + } else { + if (authCheckPatten.matcher(auth).matches()) { + if (next != null) { + next.startCall(call, headers) + } else { + NoOpListener() + } + } else { + call.close(Status.UNAUTHENTICATED, Metadata()) + NoOpListener() + } + } + } +} + +class NoOpListener : ServerCall.Listener() + class CalculatorService : CalculatorGrpcKt.CalculatorCoroutineImplBase() { override suspend fun calculateOne(request: AreaCalculator.ShapeMessage): AreaCalculator.AreaResponse { val area = when (request.shapeCase) { diff --git a/examples/gRPC/provider-states/provider-jvm/server/src/test/java/io/pact/example/grpc/provider/PactVerificationTest.java b/examples/gRPC/provider-states/provider-jvm/server/src/test/java/io/pact/example/grpc/provider/PactVerificationTest.java index f31b7d99..c68a5898 100644 --- a/examples/gRPC/provider-states/provider-jvm/server/src/test/java/io/pact/example/grpc/provider/PactVerificationTest.java +++ b/examples/gRPC/provider-states/provider-jvm/server/src/test/java/io/pact/example/grpc/provider/PactVerificationTest.java @@ -53,6 +53,10 @@ void pactVerificationTestTemplate(PactVerificationContext context) { @State("a rectangle") Map rectangleState() { LOGGER.debug("!!! rectangleState state called !!!"); - return Map.of("rectangleWidth", 5, "rectangleLength", 10); + return Map.of( + "rectangleWidth", 5, + "rectangleLength", 10, + "Auth", "CCV001" + ); } }