Generating JHipster Gateway without server code but with client websocket support - websocket

I'm using JHipster to generate a microservice application made of
a gateway application with UI and without server: option --skip-server
a gateway application without UI and with server: option --skip-client
some microservices (type microservice
In order to generate everything I'm using a jdl file which looks like this:
application {
config {
applicationType gateway,
websocket spring-websocket,
clientFramework angularX,
skipServer true,
....
}
entities *
}
application {
config {
applicationType gateway,
packageName com.fginc.hermes.front,
websocket spring-websocket,
skipClient true
...
}
entities *
}
application {
config {
applicationType microservice,
}
entities A,B
}
application {
config {
applicationType microservice,
}
entities B,C
}
/** my entities **/
Everything works fine excepts that I would like the Angular websocket code (user-tracker) to be generated on the pure UI application.
Thank you for your help

Related

Spring doc oauth2RedirectUrl with wrong server

I'm writing my first question, please be patient if anything is missing. Any Feedback is welcome.
We have an issue with the generated oauth2RedirectUrl after migrating from a Windows 2012 R2 to a Windows 2016. The server is no longer containing the alias name but the technical server name.
We are using Spring Boot 2.6.0 including spring-boot-starter-oauth2-resource-server and springdoc-openapi-ui 1.5.12.
Spring Boot is set up as a resource server with JWT authorization with Azure OAuth2.
We deploy a FAT Jar on windows machines and recently changed to a new server.
Each machine is has a DNS entry with a technical name and a human readable alias.
Lets say
human-readable-name1 for the old machine
human-readable-name2 for the new machine
We deployed the exact same software on both machines. We access the sawagger ui
with
https://human-readable-name1.domain:port/swagger-ui.html
and
https://human-readable-name2.domain:port/swagger-ui.html
The response from both machines include the generated api-docs which are looking fine.
Containing on both machines like
"servers": [
{
"url": "https://human-readable-name.domain:port",
"description": "Generated server url"
}
]
But the swagger-config change
OLD
{
"configUrl": "/v3/api-docs/swagger-config",
"oauth2RedirectUrl": "https://human-readable-name1:port/swagger-ui/oauth2-redirect.html",
"url": "/v3/api-docs",
"validatorUrl": ""
}
NEW
{
"configUrl": "/v3/api-docs/swagger-config",
"oauth2RedirectUrl": "https://technicalname:port/swagger-ui/oauth2-redirect.html",
"url": "/v3/api-docs",
"validatorUrl": ""
}
This breaks the log in as the token is no longer reaching the swagger ui client.
Any suggestions where I even can start looking for the reason?
Swagger config:
#Configuration
class OpenApiConfig {
#Value("${springdoc.oAuthFlow.authorizationUrl}")
private String authorizationUrl;
#Value("${springdoc.oAuthFlow.tokenUrl}")
private String tokenUrl;
#Bean
public OpenAPI customOpenAPI() {
return new OpenAPI()
.components(new Components()
.addSecuritySchemes("security_auth",
new SecurityScheme()
.type(SecurityScheme.Type.OAUTH2)
.flows(new OAuthFlows()
.authorizationCode(new OAuthFlow()
.authorizationUrl(authorizationUrl)
.tokenUrl(tokenUrl)
.scopes(new Scopes()
.addString("xxx", "xxx")
)
)
)
)
)
.addSecurityItem(new SecurityRequirement().addList("security_auth"))
;
}
}
Spring Boot yaml
spring.security.oauth2.resourceserver.jwt:
jwk-set-uri: https://login.microsoftonline.com/common/discovery/v2.0/keys
issuer-uri: https://login.microsoftonline.com/xxx/v2.0
springdoc:
swagger-ui:
oauth:
clientId: 'xxx'
use-pkce-with-authorization-code-grant: true
oAuthFlow:
authorizationUrl: https://login.microsoftonline.com/xxx/oauth2/v2.0/authorize
tokenUrl: https://login.microsoftonline.com/xxx/oauth2/v2.0/token

Spring Cloud Sleuth: Export data from remote system to local Zipkin

In my Spring Boot application I use spring-cloud-starter-sleuth (Version Hoxton.SR10) for tracing. It is (still) a monolithic application so I widely use the #NewSpan annotation for creating new spans.
In my development environment I also use spring-cloud-starter-zipkin, which works great.
But on the servers of our customers I don't have access to any Zipkin server nor am allowed to install one. Is there any possibility to save the data Spring is sending to Zipkin and import that to my local Zipkin server?
Solution thanks to Marcin's inspiration:
#Configuration
#ConditionalOnProperty(name = "custom.property", havingValue = "true")
public class SleuthConfiguration {
#Bean("zipkinSender")
Sender restTemplateSender() {
return new Sender() {
public Encoding encoding() { return Encoding.JSON; }
public int messageMaxBytes() { return Integer.MAX_VALUE; }
public int messageSizeInBytes(List<byte[]> list) { return Integer.MAX_VALUE; }
#Override
public Call<Void> sendSpans(List<byte[]> list) {
String result = convertByteArrayToList(list);
saveToFile(result);
return new Call.Base<Void>() {...};
}
};
}
}
Implement convertByteArrayToList and saveToFile your own, because my solution depends on custom libraries.
You could create your own SpanHandler bean that takes the FinishedSpan, converts into JSON and stores it somewhere on your drive. Then you could just iterate over jsons and upload them to the Zipkin server

How to consume basic-authentication protected Restful web service via REACTIVE feign client

#ReactiveFeignClient(name = "service.b",configuration = CustomConfiguration.class)
public interface FeingConfiguration {
#PostMapping("/api/students/special")
public Flux<Student> getAllStudents(#RequestBody Flux<SubjectStudent> lista);
}
Help, how can I add a basic authentication to my header that I have in the service: service.b.
I have the CustomConfiguration.class class but it doesn't allow me, I have 401 authorization failed
#Configuration
public class CustomConfiguration {
#Bean
public BasicAuthRequestInterceptor basic() {
return new BasicAuthRequestInterceptor("user","user") ;
}
Looks like you are trying to use feign-reactive (https://github.com/Playtika/feign-reactive) to implement your REST clients. I am also using it for one of my projects and it looks like this library does not have an out-of-the-box way to specify basic auth credentials. At least no way to do this declaratively. So I didn't find a better way to do this than to abandon the auto-configuration via #ReactiveFeignClient and start configuring reactive feign clients manually. This way you can manually add "Authorization" header to all outgoing requests. So, provided this client definition:
public interface FeingClient {
#PostMapping("/api/students/special")
public Flux<Student> getAllStudents(#RequestBody Flux<SubjectStudent> lista);
}
Add the following configuration class to your Spring context, replacing username, password and service-url with your own data:
#Configuration
public class FeignClientConfiguration {
#Bean
FeignClient feignClient() {
WebReactiveFeign
.<FeignClient>builder()
.addRequestInterceptor(request -> {
request.headers().put(
"Authorization",
Collections.singletonList(
"Basic " + Base64.getEncoder().encodeToString(
"username:password".getBytes(StandardCharsets.ISO_8859_1))));
return request;
})
.target(FeignClient.class, "service-url");
}
}
Note, that this API for manual configurftion of reactive feign clients can differ between different versions of the reactive-feign library. Also note that this approach has a major drawback - if you start creating beans for your feign clients manually you lose the main advantage of Feign - ability to write REST-clients declaratively with just a few lines of code. E.g. if you want to use the above client with some sort of client-side load-balancing mechanism, like Ribbon/Eureka or Ribbon/Kubernetes, you will also need to configure that manually.
You can use a direct interceptor:
#Configuration
class FeignClientConfiguration {
#Bean
fun reactiveHttpRequestInterceptor(): ReactiveHttpRequestInterceptor {
return ReactiveHttpRequestInterceptor { request: ReactiveHttpRequest ->
request.headers()["Authorization"] = //insert data from SecurityContextHolder;
Mono.just(request)
}
}
}

Feign with RibbonClient and Consul discovery without Spring Cloud

I was trying to setup Feign to work with RibbonClient, something like MyService api = Feign.builder().client(RibbonClient.create()).target(MyService.class, "https://myAppProd");, where myAppProd is an application which I can see in Consul. Now, if I use Spring annotations for the Feign client (#FeignClient("myAppProd"), #RequestMapping), everything works as Spring Cloud module will take care of everything.
If I want to use Feign.builder() and #RequestLine, I get the error:
com.netflix.client.ClientException: Load balancer does not have available server for client: myAppProd.
My first initial thought was that Feign was built to work with Eureka and only Spring Cloud makes the integration with Consul, but I am unsure about this.
So, is there a way to make Feign work with Consul without Spring Cloud?
Thanks in advance.
In my opinion, it's not feign work with consul, its feign -> ribbon -> consul.
RibbonClient needs to find myAppProd's serverList from its LoadBalancer.
Without ServerList, error: 'does not have available server for client'.
This job has been done by SpringCloudConsul and SpringCloudRibbon project, of course you can write another adaptor, it's just some glue code. IMHO, you can import this spring dependency into your project, but use it in non-spring way . Demo code:
just write a new feign.ribbon.LBClientFactory, that generate LBClient with ConsulServerList(Spring's class).
public class ConsulLBFactory implements LBClientFactory {
private ConsulClient client;
private ConsulDiscoveryProperties properties;
public ConsulLBFactory(ConsulClient client, ConsulDiscoveryProperties consulDiscoveryProperties) {
this.client = client;
this.properties = consulDiscoveryProperties;
}
#Override
public LBClient create(String clientName) {
IClientConfig config =
ClientFactory.getNamedConfig(clientName, DisableAutoRetriesByDefaultClientConfig.class);
ConsulServerList consulServerList = new ConsulServerList(this.client, properties);
consulServerList.initWithNiwsConfig(config);
ZoneAwareLoadBalancer<ConsulServer> lb = new ZoneAwareLoadBalancer<>(config);
lb.setServersList(consulServerList.getInitialListOfServers());
lb.setServerListImpl(consulServerList);
return LBClient.create(lb, config);
}
}
and then use it in feign:
public class Demo {
public static void main(String[] args) {
ConsulLBFactory consulLBFactory = new ConsulLBFactory(
new ConsulClient(),
new ConsulDiscoveryProperties(new InetUtils(new InetUtilsProperties()))
);
RibbonClient ribbonClient = RibbonClient.builder()
.lbClientFactory(consulLBFactory)
.build();
GitHub github = Feign.builder()
.client(ribbonClient)
.decoder(new GsonDecoder())
.target(GitHub.class, "https://api.github.com");
List<Contributor> contributors = github.contributors("OpenFeign", "feign");
for (Contributor contributor : contributors) {
System.out.println(contributor.login + " (" + contributor.contributions + ")");
}
}
interface GitHub {
#RequestLine("GET /repos/{owner}/{repo}/contributors")
List<Contributor> contributors(#Param("owner") String owner, #Param("repo") String repo);
}
public static class Contributor {
String login;
int contributions;
}
}
you can find this demo code here, add api.github.com to your local consul before running this demo.

Invoking remote service using JMS

I have two projects one the service project another one consumer project,
Consumer project consumes the services of other project and the call should be async using JMS
I installed jms plugin in both of the projects
I have defined the JMSConnectionFactory in both of the project as below in resources.groovy
import org.springframework.jms.connection.SingleConnectionFactory
import org.apache.activemq.ActiveMQConnectionFactory
beans = {
jmsConnectionFactory(org.apache.activemq.ActiveMQConnectionFactory) { brokerURL = 'vm://localhost' }
}
Note: Both of the project are for now on same machine (i.e. localhost)
Now from consumer's controller I am making call to service from ServiceProvider project
jmsService.send(service:'serviceProvider', params.body)
In ServiceProvider the service is defined as follow
import grails.plugin.jms.*
class ServiceProviderService {
def jmsService
static transactional = true
static exposes = ['jms1']
def createMessage(msg) {
print "Called1"
sleep(2000) // slow it down
return null
}
}
now when controller submits the call to service it gets submitted successfully but doesn't reach to the actual service
I also tried
jmsService.send(app: "ServiceProvider", service: "serviceProvider", method: "createMessage", msg, "standard", null)
Update
Now I have installed activeMQ plugin to service provider to make it embedded broker (jms is already there)
and created a service
package serviceprovider
class HelloService {
boolean transactional = false
static exposes = ['jms']
static destination = "queue.notification"
def onMessage(it){
println "GOT MESSAGE: $it"
}
def sayHello(String message){
println "hello"+message
}
}
resources.groovy in both of the project is now
import org.springframework.jms.connection.SingleConnectionFactory
import org.apache.activemq.ActiveMQConnectionFactory
beans = {
jmsConnectionFactory(org.apache.activemq.ActiveMQConnectionFactory) { brokerURL = 'tcp://127.0.0.1:61616' }
}
from consumer's controller I am calling this service like below
jmsService.send(app:'queue.notification',service:'hello',method: 'sayHello', params.body)
call to method gets submitted but actually it is not getting called!
The in vm activemq config (vm://localhost) works only within a single VM. If your 2 projects run in separate VMs try setting up an external AMQ broker.
if you are using separate processes, then you need to use a different transport than VM (its for a single VM only), also, is one of your processes starting a broker? If not, then one of them should embed the broker (or run it externally) and expose it over a transport (like TCP)...

Resources