How to load application properties dynamically in #HystrixProperty annotation - spring

Working method with hardcoded values for #HystricProperty:
#HystrixCommand(ignoreExceptions={HttpClientErrorException.class},
//groupKey="ProductServiceGroup",commandKey = "test", threadPoolKey = "ProductInfoDetailsThreadPool",
commandProperties = {
#HystrixProperty(name=HystrixPropertiesManager.CIRCUIT_BREAKER_SLEEP_WINDOW_IN_MILLISECONDS, value="500"),
#HystrixProperty(name=HystrixPropertiesManager.EXECUTION_ISOLATION_THREAD_TIMEOUT_IN_MILLISECONDS, value="1500"),
#HystrixProperty(name=HystrixPropertiesManager.CIRCUIT_BREAKER_ENABLED, value="true"),
#HystrixProperty(name=HystrixPropertiesManager.CIRCUIT_BREAKER_REQUEST_VOLUME_THRESHOLD, value="20"),
#HystrixProperty(name=HystrixPropertiesManager.EXECUTION_ISOLATION_THREAD_INTERRUPT_ON_TIMEOUT, value="true"),
#HystrixProperty(name=HystrixPropertiesManager.EXECUTION_ISOLATION_SEMAPHORE_MAX_CONCURRENT_REQUESTS, value="20")
},
fallbackMethod = "reliable")
public Map readingList() {
try {
Thread.sleep(950);
} catch (InterruptedException e) {
e.printStackTrace();
}
URI uri = URI.create("http://localhost:8090/recommended");
return this.restTemplate.getForObject(uri, Map.class);
}
I don't want to hardcode these values in #HystrixProperty annotation, instead want to read these properties from application.properties.
some thing like this:
#HystrixProperty(name=HystrixPropertiesManager.EXECUTION_ISOLATION_THREAD_TIMEOUT_IN_MILLISECONDS, value="${timeout.in.millis}")

you could use a properties placeholder.
Define it in your spring configuration
<context:property-placeholder location="classpath:myapp.properties" />
then create the properties file myapp.propertiesand put in in your classpath, as referenced in the configuration. the content could be
CIRCUIT_BREAKER_SLEEP_WINDOW_IN_MILLISECONDS_VALUE=500
EXECUTION_ISOLATION_THREAD_TIMEOUT_IN_MILLISECONDS_VALUE=1500
and so on...
then you can use that parameters in your #HistrixCommand just as you wrote
HystrixCommand(ignoreExceptions={HttpClientErrorException.class},
//groupKey="ProductServiceGroup",commandKey = "test", threadPoolKey = "ProductInfoDetailsThreadPool",
commandProperties = {
#HystrixProperty(name=HystrixPropertiesManager.CIRCUIT_BREAKER_SLEEP_WINDOW_IN_MILLISECONDS, value=${CIRCUIT_BREAKER_SLEEP_WINDOW_IN_MILLISECONDS_VALUE}),
#HystrixProperty(name=HystrixPropertiesManager.EXECUTION_ISOLATION_THREAD_TIMEOUT_IN_MILLISECONDS, value=${EXECUTION_ISOLATION_THREAD_TIMEOUT_IN_MILLISECONDS_VALUE}),
...

Related

Spring Boot - get current configuration file path in runtime

I need to get absolute path to current active configuration file in Spring boot that don't locate in classpath or resources
It can be located in default place - project folder, subfolder "config", set via spring.config.location and in random place, also in another disk
Something like "E:\projects\configs\myProject\application.yml"
Assume that you have these application-{env}.yml config profiles in resources folder, and we are going to activate the dev configuration.
application.yml
application-dev.yml
application-prod.yml
application-test.yml
...
There are two ways you can activate the dev :
modify your application.yml,
spring:
profiles:
active: dev
or by command line when you want to start your application :
java -jar -Dspring.profiles.active=dev application.jar
Then, try this code in your program:
// get the active config dynamically
#Value("${spring.profiles.active}")
private String activeProfile;
public String readActiveProfilePath() {
try {
URL res = getClass().getClassLoader().getResource(String.format("application-%s.yml", activeProfile));
if (res == null) {
res = getClass().getClassLoader().getResource("application.yml");
}
File file = Paths.get(res.toURI()).toFile();
return file.getAbsolutePath();
} catch (Exception e) {
// log the error.
return "";
}
}
The output will be an absolute path of application-dev.yml
Someday I found same question here, but cant find it now
So here my solution, maybe someone needs it
#Autowired
private ConfigurableEnvironment env;
private String getYamlPath() throws UnsupportedEncodingException {
String projectPath = System.getProperty("user.dir");
String decodedPath = URLDecoder.decode(projectPath, "UTF-8");
//Get all properies
MutablePropertySources propertySources = env.getPropertySources();
String result = null;
for (PropertySource<?> source : propertySources) {
String sourceName = source.getName();
//If configuration loaded we can find properties in environment with name like
//"Config resource '...[absolute or relative path]' via ... 'path'"
//If path not in classpath -> take path in brackets [] and build absolute path
if (sourceName.contains("Config resource 'file") && !sourceName.contains("classpath")) {
String filePath = sourceName.substring(sourceName.indexOf("[") + 1, sourceName.indexOf("]"));
if (Paths.get(filePath).isAbsolute()) {
result = filePath;
} else {
result = decodedPath + File.separator + filePath;
}
break;
}
}
//If configuration not loaded - return default path
return result == null ? decodedPath + File.separator + YAML_NAME : result;
}
Not the best solution I suppose but it works
If you have any idea how to improve it I would really appreciate it

How to specify templateDir in Swagger Codegen (Gradle) for OpenAPI 3.0?

Anyone have any idea how to specify a templateDir for swagger codegen v3? I have attached a snippet of my build.gradle below
Also the setTemplateDir does not do anything so I am guessing thats not an existing method.
My class path is io.swagger.codegen.v3:swagger-codegen-maven-plugin:3.0.27
task generateServer {
doLast {
def openAPI = new OpenAPIV3Parser().read(rootProject.swaggerFile.toString(), null, null)
def clientOpts = new ClientOptInput().openAPI(openAPI)
def codegenConfig = CodegenConfigLoader.forName('spring')
codegenConfig.setOutputDir(project.buildDir.toString())
// codegenConfig.setTemplateDir('test');
clientOpts.setConfig(codegenConfig)
def clientOps = new ClientOpts()
clientOps.setProperties([
'dateLibrary' : 'java8', // Date library to use
'useTags' : 'true', // Use tags for the naming
'interfaceOnly' : 'true' // Generating the Controller API interface and the models only
])
clientOpts.setOpts(clientOps)
def generator = new DefaultGenerator().opts(clientOpts)
generator.generate() // Executing the generation
}
}
I figured this out. I would need to create a configurator and set the template directory there
Also so you know, if a mustache file is missing, then it will be pulled from one of dependency jars
task generateServer {
doLast {
CodegenConfigurator codegenConfigurator = new CodegenConfigurator()
codegenConfigurator
.setTemplateDir(rootProject.templateDir.toString())
.setLang('spring')
.setOutputDir(project.buildDir.toString())
.setInputSpec(rootProject.swaggerFile.toString())
OpenAPI openAPI = new OpenAPIV3Parser().read(rootProject.swaggerFile.toString(), null, null)
ClientOptInput clientOptInput = codegenConfigurator.toClientOptInput().openAPI(openAPI)
DefaultGenerator generator = new DefaultGenerator().opts(clientOptInput)
generator.generate() // Executing the generation*/
}
}

How to start GraphQL server when running .net5 integration tests?

I believe I am missing/misunderstanding something fundamental about the way .net5 works. In setting up an integration test environment for my GraphQL API, I am missing the step on how to start the GraphQL server from said test environment.
When I run the main project, the server is started properly and I can navigate to localhost in the browser and successfully execute GraphQL queries/mutations. My goal here is to set up some automated integration tests.
I'm using NUnit as my test runner and am using WebApplicationFactory<Startup> to "start the server" as I understand it.
In my test project, I'm under the impression that WebApplicationFactory<Startup> is supposed to basically use the Startup.cs class from my main project in my test project so that I don't have to duplicate all the settings, configurations, and injected services. Please correct me if that assumption is not correct.
I've pasted the code I think is relevant.
ApiWebApplicationFactory<Startup>
public class ApiWebApplicationFactory<TStartup> : WebApplicationFactory<Startup> where TStartup : class
{
public static IConfigurationRoot Configuration { get; set; }
public ApiWebApplicationFactory()
{
var configBuilder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
Configuration = configBuilder.Build();
}
protected override void ConfigureClient(HttpClient client)
{
base.ConfigureClient(client);
client.BaseAddress = new Uri("https://localhost");
client.Timeout = new TimeSpan(0, 10, 0);
}
// Based on my assumption this class reuses everything in the Startup.cs class
// I don't actually think this is necessary, but thought it was worth trying
// the test with and without this code.
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{
services
.AddGraphQLServer()
.AddQueryType<Query>()
.AddMutationType<Mutation>()
.AddType<GraphQLContentItem>()
.AddType<GraphQLFolder>();
});
}
}
OneTimesetUp
[OneTimeSetUp]
public void OneTimeSetUp()
{
_factory = new ApiWebApplicationFactory<Startup>();
_client = _factory.WithWebHostBuilder(builder =>
{
builder.ConfigureServices(services =>
{
services.AddScoped<ICacheRepository, MockCache>();
});
}).CreateClient();
var connString = ApiWebApplicationFactory<Startup>.Configuration.GetConnectionString("DefaultConnection");
var options = new DbContextOptionsBuilder<CmsContext>()
.UseMySql(connString, ServerVersion.AutoDetect(connString))
.Options;
_dbContext = new CmsContext(options);
_dbContext.Database.EnsureCreated();
}
Test
[Test]
public async Task Test()
{
// If I set a breakpoint here, I can't navigate to the URL like I'm expecting to
var graphQLHttpClient =
new GraphQLHttpClient(
new GraphQLHttpClientOptions { EndPoint = new Uri("https://localhost/graphql") },
new NewtonsoftJsonSerializer(),
_client);
var request = new GraphQLRequest
{
Query = #"
query GetCurrentSession() {
getCurrentSession() {
id
name
}
}",
OperationName = "GetCurrentSession"
};
// Error is thrown here with "Bad Request"
var response = await graphQLHttpClient.SendQueryAsync<Session>(request);
// Further code is omitted
}
Please let me know if you see what I am missing. Thanks in advance~

consume/return a JSON response when executing flow using Spring boot API

I'm a beginner in corda and I'm trying to execute flows using Spring boot API. When I used:
#PostMapping(value = [ "create-iou" ], produces = [ TEXT_PLAIN_VALUE ] , headers = [ "Content-Type=application/x-www-form-urlencoded" ])
my flow is getting executed (by testing it using insomnia). But When I changed it to
#PostMapping(value = [ "create-iou" ], produces = [ APPLICATION_JSON_VALUE ], headers = [ "Content-Type=application/json" ])
It gives me a 406 not acceptable error: No body returned for response.
Here's the API I've created/copied:
#PostMapping(value = [ "create-iou" ], produces = [ TEXT_PLAIN_VALUE ] , headers = [ "Content-Type=application/x-www-form-urlencoded" ])
fun createIOU(request: HttpServletRequest): ResponseEntity<String> {
val iouValue = request.getParameter("iouValue").toInt()
val partyName = request.getParameter("partyName")
?: return ResponseEntity.badRequest().body("Query parameter 'partyName' must not be null.\n")
if (iouValue <= 0 ) {
return ResponseEntity.badRequest().body("Query parameter 'iouValue' must be non-negative.\n")
}
val partyX500Name = CordaX500Name.parse(partyName)
val otherParty = proxy.wellKnownPartyFromX500Name(partyX500Name) ?: return ResponseEntity.badRequest().body("Party named $partyName cannot be found.\n")
return try {
val signedTx = proxy.startTrackedFlow(::Initiator, iouValue, otherParty).returnValue.getOrThrow()
ResponseEntity.status(HttpStatus.CREATED).body("Transaction id ${signedTx.id} committed to ledger.\n")
} catch (ex: Throwable) {
logger.error(ex.message, ex)
ResponseEntity.badRequest().body(ex.message!!)
}
}
I would like to return something like this:
{
iouValue: 99,
lender: PartyA,
borrower: PartyB
}
When executing the flow using http endpoint.
You need to use the RPC connection libraries provided by Corda:
import net.corda.client.rpc.CordaRPCClient
import net.corda.client.rpc.CordaRPCConnection
Take a look to this example to see how to use them.
You are not showing how your proxy is instantiate, but you need to instantiate a proxy to connect via RPC to the node, like so:
val rpcAddress = NetworkHostAndPort(host, rpcPort)
val rpcClient = CordaRPCClient(rpcAddress)
val rpcConnection = rpcClient.start(username, password)
proxy = rpcConnection.proxy
and once you have the proxy, you can create SpringBoot APIs to call that proxy that makes the RPC calls:
#RestController
#RequestMapping("/")
class StandardController(rpc: NodeRPCConnection) {
private val proxy = rpc.proxy
#GetMapping(value = ["/addresses"], produces = arrayOf("text/plain"))
private fun addresses() = proxy.nodeInfo().addresses.toString()
#GetMapping(value = ["/identities"], produces = arrayOf("text/plain"))
private fun identities() = proxy.nodeInfo().legalIdentities.toString()

How/where do I register the IDbPerTenantConnectionStringResolver

Trying to run core api host. i have this in the EntityFrameworkModule
public override void PreInitialize()
{
Configuration.MultiTenancy.IsEnabled = true;`
// CONNECTION STRING RESOLVER
Configuration.ReplaceService<IConnectionStringResolver, DbPerTenantConnectionStringResolver>(DependencyLifeStyle.Transient);
Configuration.DefaultNameOrConnectionString = MyConsts.DefaultConnectionStringName;
if (!SkipDbContextRegistration)
{
//DEFAULT
Configuration.Modules.AbpEfCore().AddDbContext<MyContext>(options =>
{
if (options.ExistingConnection != null)
MyContextConfigurer.Configure(options.DbContextOptions, options.ExistingConnection);
else
MyContextConfigurer.Configure(options.DbContextOptions, options.ConnectionString);
});
}
}
ERROR
Mvc.ExceptionHandling.AbpExceptionFilter - Can't create component 'Ccre.EntityFrameworkCore.AbpZeroDbMigrator' as it has dependencies to be satisfied.
'Ccre.EntityFrameworkCore.AbpZeroDbMigrator' is waiting for the following dependencies:
- Service 'Abp.MultiTenancy.IDbPerTenantConnectionStringResolver' which was not registered.
How/where do I register the IDbPerTenantConnectionStringResolver?
I have this line in the PreInitialize of the Migrator.MigratorModule
Configuration.ReplaceService<IConnectionStringResolver, DbPerTenantConnectionStringResolver>(DependencyLifeStyle.Transient);
as well as in the EntityFrameworkModule

Resources