How to trigger Netflix DGS subscription by using RSocket - java

I'm practicing DGS framework's Subscription following the document, and I can trigger the subscription by using this Websocket support(Of course it can work)
implementation 'com.netflix.graphql.dgs:graphql-dgs-subscriptions-websockets-autoconfigure:latest.release'
But now I want to try configure the websocket path by myself and I'd like to use the RSocket to complete. I have no idea how to write the configuration (Wrote it in application.yml but maybe it should be written in a configuration Java class?).
I'd tried like this but the subscription didn't work at all.
application.yml
spring:
rsocket:
server:
transport: websocket
port: 7000
mapping-path: /subscriptions
And my DgsComponent
#DgsSubscription(field = "hello")
public Publisher<String> hello() {
return Flux.interval(Duration.ofSeconds(1))
.map(s -> "world" + s)
.take(10);
}
I'm new in RSocket and Netflix DGS, so if anyone could help me, thank you very much!!

Related

How to write a simple unit test for GraphQL Subscription over websocket?

My backend is a webflux server using the graphql-java-kickstart (v11.0.0) library and I have a bunch of graphql http requests right now.
I'm using Spring's WebTestClient to test my graphql Queries and Mutations.
Basically, I'll just call it and compare the response like so
webTestClient
.post()
.uri("/graphql")
.header(...)
.body(Mono.just(request), JsonNode.class)
.exchange()
.expectStatus()
.isOk()
.expectBody(JsonNode.class)
.returnResult()
.getResponseBody()
But this is over a normal HTTP and simple REST protocol.
With GraphQL Subscriptions, I need to set up a websocket and do the Apollo (subscription-transport.ws) handshake protocol before sending the Subscription request in, and I have to wait for a response async.
I have manually verified the subscription works with the WebSocket Test Client chrome extension
What's the correct way to write a unit test for this?
Suppose I have a simple Subscription that just returns the timestamp back every second
type Subscription {
clock: String!
}
How do I write a unit test for this? What library or client should I use?
A link or example would be awesome!

Access service's endpoint /actuator/health through Spring Cloud Netflix: Zuul

I have Spring gateway: localhost:7856 and microservice - "my-service", for example localhost:8081. I can get access to endpoint localhost:8081/actuator/health -> {"status": "UP"}. But I need to access such endpoint through gateway like localhost:7856/my-service/actuator/health
My gateway config:
zuul:
ignoredServices: '*'
routes:
my-service:
path: /my-service/**
serviceId: my-service
stripPrefix: false
Here, crucial moment, I can't change stripPrefix to true. I know, that I can add
management:
endpoints:
web:
base-path: /my-service/actuator
but it wouldn't be good solution, because in that case also need to change eureka config (for eureka default endpoint is service-name/actuator/health) for check health status for microservices. Or I can create additional endpoint that would redirect to what I need. But I'm trying to find the best decision, may be its a special property for zuul or overriding zuul classes ?
Finally, I found a solution.
import org.springframework.cloud.netflix.zuul.filters.Route;
import org.springframework.cloud.netflix.zuul.filters.SimpleRouteLocator;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties.ZuulRoute;
public class CustomRouteLocator extends SimpleRouteLocator {
public CustomRouteLocator(String servletPath, ZuulProperties properties) {
super(servletPath, properties);
}
#Override
protected Route getRoute(ZuulRoute route, String path) {
boolean oldPrefix = route.isStripPrefix();
if (path.matches(".*/actuator/.*")) {
route.setStripPrefix(true);
}
Route resultRoute = super.getRoute(route, path);
route.setStripPrefix(oldPrefix);
return resultRoute;
}
}
I turn on/off stripPrefix property. That works well.

Connect to Azure EventHub(Kafka like) with Spring Boot using connection string

I need to connect to event hub with enabled kafka with Spring Boot, and I have connection string and name space where should I connect.
I'm using such dependencies
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>spring-cloud-azure-eventhubs-stream-binder</artifactId>
<version>1.2.7</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream-binder-kafka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream</artifactId>
</dependency>
I found a tutorial where I need to login into azure from my local machine with az login and create auth file, BUT I was provided with connection string which should I use, so is there any way to specify
ONLY connection string with namespace like this :
spring.cloud.azure.eventhub.connection-string
spring.cloud.azure.eventhub.namespace
Because now it is complaining that is is missing resource-group.
How should I connect to EventHub?
tl;dr
My question contains incorrect dependencies, I've added two binders, which incorrect. When you start app spring cloud stream don't know what is primary. So you need to choose only one.
So as I want to work with Event Hub, but had not previous experience with it, but had experience with Kafka and Event Hub has mode to work by Kafka protocol I started to look in that way. All tutorial from Microsoft are not working (sad for me). They are outdated.
So, I started to think if it is working by Kafka protocol, maybe I can thread Event Hub as simple Kafka with some configuration changes. After googling I found a lot of tutorial how to do it.
All you need is to create regular Kafka consumer/producer. I've done it with Spring Cloud Stream
#Slf4j
#EnableBinding(Sink.class)
public class KafkaSink {
#StreamListener(Sink.INPUT)
public void consumerMessage(TestMessage testMessage) {
log.info("{}", testMessage);
}
}
#Component
#EnableBinding(Source.class)
public class KafkaSource {
private final MessageChannel output;
#Autowired
public KafkaSource(MessageChannel output) {
this.output = output;
}
public void send(TestMessage testMessage) {
output.send(MessageBuilder.withPayload(testMessage).build());
}
}
And then just add proper jaas configuration into application.* file. You need to get connection string for your Event Hub
My yaml file:
spring:
cloud:
stream:
bindings:
input:
destination: my-topic
output:
destination: my-topic
kafka:
binder:
auto-create-topics: true
brokers: ${EVENT_HUB_KAFKA_BROKER}
configuration:
sasl:
jaas:
config: ${EVENT_HUB_CONNECTION_STRING}
mechanism: PLAIN
security:
protocol: SASL_SSL
One important thing EVENT_HUB_KAFKA_BROKER should be Event Hub address, something like blablabla.servicebus.windows.net:9093 (don't forget port). For EVENT_HUB_CONNECTION_STRING you hould specify module which will be parsing connection string as password and it should be something like org.apache.kafka.common.security.plain.PlainLoginModule required username="$ConnectionString" password="{your_connection_string}"\

Web service putting Soap message in queue with Spring Integration and Jms

I want to use Spring Integration to expose a simple web service that pushes incoming message into ActiveMQ and responds immediately. My go-to solution was MarshallingWebServiceInboundGateway connected to Jms.outboundAdapter with IntegrationFlow. Below the Gateway and IntegrationFlow snippets. Problem with this is Adapter does not provide response (duh) which Gateway expects. The response I get back from the service is empty 202, with delay of about 1500ms. This is caused by a reply timeout I see in TRACE logs:
"2020-04-14 17:17:50.101 TRACE 26524 --- [nio-8080-exec-6] o.s.integration.core.MessagingTemplate : Failed to receive message from channel 'org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel#518ffd27' within timeout: 1000"
No hard exceptions anywhere. The other problem is I cannot generate the response myself. I can't add anything to IntegrationFlow after the .handle with Adapter.
Any other way I can try to fulfill the scenario?
How, if at all possible, can I generate and return response in situation there is no better approach?
Most likely the proper way would be to use Gateways on both ends, but this is not possible. I cannot wait with response until message in the queue gets consumed and processed.
'''
#Bean
public MarshallingWebServiceInboundGateway greetingWebServiceInboundGateway() {
MarshallingWebServiceInboundGateway inboundGateway = new MarshallingWebServiceInboundGateway(
jaxb2Marshaller()
);
inboundGateway.setRequestChannelName("greetingAsync.input");
inboundGateway.setLoggingEnabled(true);
return inboundGateway;
}
#Bean
public IntegrationFlow greetingAsync() {
return f -> f
.log(LoggingHandler.Level.INFO)
.handle(Jms.outboundAdapter(this.jmsConnectionFactory)
.configureJmsTemplate(c -> {
c.jmsMessageConverter(new MarshallingMessageConverter(jaxb2Marshaller()));
})
.destination(JmsConfig.HELLO_WORLD_QUEUE));
}
'''
The logic and assumptions are fully correct: you can't return after one-way handle() and similar to that Jms.outboundAdapter().
But your problem that you fully miss one of the first-class citizens in Spring Integration - a MessageChannel. It is important to understand that even in the flow like yours there are channels between endpoints (DSL methods) - implicit (DirectChannel), like in your case, or explicit: when you use a channel() in between and can place there any possible implementation: https://docs.spring.io/spring-integration/docs/5.3.0.M4/reference/html/dsl.html#java-dsl-channels
One of the crucial channel implementation is a PublishSubscribeChannel (a topic in JMS specification) when you can send the same message to several subscribed endpoints: https://docs.spring.io/spring-integration/docs/5.3.0.M4/reference/html/core.html#channel-implementations-publishsubscribechannel
In your case the fists subscriber should be your existing, one-way Jms.outboundAdapter(). And another something what is going to generate response and reply it into a replyChannel header.
For this purpose Java DSL provides a nice hook via sub-flows configuration: https://docs.spring.io/spring-integration/docs/5.3.0.M4/reference/html/dsl.html#java-dsl-subflows
So, some sample of publish-subscriber could be like this:
.publishSubscribeChannel(c -> c
.subscribe(sf -> sf
.handle(Jms.outboundAdapter(this.jmsConnectionFactory))))
.handle([PRODUCE_RESPONSE])

Spring Boot + Cloud | Zuul Proxy | Integration testing

When working with Spring Boot to build micro-services its very easy to write extensive and very readable integration tests and mock remote service requests with MockRestServiceServer.
Is there a way to use similar approach to perform additional integration test on ZuulProxy? What I would like to achieve is being able to mock remote servers that ZuulProxy would forward to and validate that all of my ZuulFitlers behaved as expected. However, ZuulProxy is using RestClient from Netflix (deprecated it would seem?) which naturally does not use RestTemplate which could be re-configured by MockRestServiceServer and I currently can't find a good way of mocking responses from remote services for proxied requests.
I have a micro-service that is responsible for handling API Session Key creation and then will act similar to an API Gateway. Forwarding is done with Zuul Proxy to underlying exposed services, and Zuul Filters will detect if Session key is valid or not. An integration test would therefore create a valid session and then forward to a fake endpoint, e.g 'integration/test'.
Specifying that 'integration/test' is a new endpoint is possible by setting a configuration property on #WebIntegrationTest, I can successfully mock all services that are being handled via RestTemplate but not Zuul forwarding.
What's the best way to do achieve mocking of a forward target service?
Check out WireMock. I have been using it to do integration level testing of my Spring Cloud Zuul project.
import static com.github.tomakehurst.wiremock.client.WireMock.*;
public class TestClass {
#Rule
public WireMockRule serviceA = new WireMockRule(WireMockConfiguration.options().dynamicPort());
#Before
public void before() {
serviceA.stubFor(get(urlPathEqualTo("/test-path/test")).willReturn(aResponse()
.withHeader("Content-Type", "application/json").withStatus(200).withBody("serviceA:test-path")));
}
#Test
public void testRoute() {
ResponseEntity<String> responseEntity = this.restTemplate.getForEntity("/test-path/test", String.class);
assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK);
serviceA.verify(1, getRequestedFor(urlPathEqualTo("/test-path/test")));
}
}
The accepted answer has the main idea. But I struggle on some points until figure out the problem. So I would like to show a more complete answer using also Wiremock.
The test:
#ActiveProfiles("test")
#TestPropertySource(locations = "classpath:/application-test.yml")
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#AutoConfigureWireMock(port = 5001)
public class ZuulRoutesTest {
#LocalServerPort
private int port;
private TestRestTemplate restTemplate = new TestRestTemplate();
#Before
public void before() {
stubFor(get(urlPathEqualTo("/1/orders/")).willReturn(aResponse()
.withHeader("Content-Type", MediaType.TEXT_HTML_VALUE)
.withStatus(HttpStatus.OK.value())));
}
#Test
public void urlOrders() {
ResponseEntity<String> result = this.restTemplate.getForEntity("http://localhost:"+this.port +"/api/orders/", String.class);
assertEquals(HttpStatus.OK, result.getStatusCode());
verify(1, getRequestedFor(urlPathMatching("/1/.*")));
}
}
And the application-test.yml:
zuul:
prefix: /api
routes:
orders:
url: http://localhost:5001/1/
cards:
url: http://localhost:5001/2/
This should work.
But Wiremock has some limitations for me. If you has proxy requests with different hostnames running on different ports, like this:
zuul:
prefix: /api
routes:
orders:
url: http://lp-order-service:5001/
cards:
url: http://lp-card-service:5002/
A localhost Wiremock running on the same port will no be able to help you. I'm still trying to find a similar Integration Test where I could just mock a Bean from Spring and read what url the Zuul Proxy choose to route before it make the request call.

Categories