Read CF environment nested JSON with #ConfigurationProperties - java

I have an application on SpringBoot that is deployes to Cloud Foundry. It has a bound service that attaches some information to VCAP_SERVICES that I can use in my application.properties file to receive security related data.
This services variables are in the user-provided sub-json of whole VCAP_SERVICE environment variable and looks like this:
"user-provided": [
{
"credentials": {
"auth-scopes": {
"group-1": "superadmin",
"group-2": "admin"
},
"base64ClientCredential": "SSBBTSBOT1QgVEhBVCBTSUxMWSA6LUQ=",
"client-id": "my-app",
"client-secret": "g1234567890",
"token-info-uri": "https://XYZ.predix.io/check_token",
"user-info-uri": "https://XYZ.predix.io/userinfo"
},
"label": "user-provided",
"name": "my-app-creds",
"syslog_drain_url": "",
"tags": [],
"volume_mounts": []
}
]
In my application.properties I have
security.properties.client-id=${vcap.services.my-app-creds.credentials.client-id}
security.properties.client-secret=${vcap.services.my-app-creds.credentials.client-secret}
security.properties.token-info-uri=${vcap.services.my-app-creds.credentials.token-info-uri}
security.properties.user-info-uri=${vcap.services.my-app-creds.credentials.user-info-uri}
security.properties.auth-scopes=${vcap.services.my-app-creds.credentials.auth-scopes}
I also created class SecurityProperties that reads those properties and stores in JAVA:
#Component
#PropertySource("classpath:application.properties")
#ConfigurationProperties(prefix = "security.properties")
public class SecurityProperties {
String clientId;
String clientSecret;
String tokenInfoUri;
String userInfoUri;
//Required getters and setters
}
And this class successfully binds those fields to information received from application.properties.
The problem occurs when I want to receive Map:
"auth-scopes": {
"group-1": "superadmin",
"group-2": "admin"
}
I tried adding to SecurityProperties class field:
HashMap<String, String> authScopes;
But it fails
I also tried with inner class
AuthScopes authScopes;
public static class AuthScopes {
//various different implementations
//to read this but no success at all
}
I ran out of other ideas. I just can't figure out how to get this. I will also accept that auth-scopes json from VCAPS will be read as a string, and then I will parse it - no problem. The issue is that even if I add to SecurityProperties field String authScopes it is bound with exactly this string: "${vcap.services.my-app-creds.credentials.auth-scopes}". Nothing useful.
If you have any idea - please share.

Spring Boot provides the vcap. properties via the CloudFoundryVcapEnvironmentPostProcessor. That class reads the VCAP_SERVICES JSON and flattens it into a set of discrete properties. If you add Spring Boot actuators to your application and access the /env endpoint, you will see the set of vcap. properties that get created.
In your example, the properties will contain these values:
"vcap": {
...
"vcap.services.my-app-creds.credentials.auth-scopes.group-1": "******",
"vcap.services.my-app-creds.credentials.auth-scopes.group-2": "******",
}
There is no vcap.services.my-service.credentials.auth-scopes property in that list, because the JSON get completely flattened. The property names use the dot notation to reflect the JSON structure, but the hierarchy is not preserved in the set of properties. In short, what you are trying to do won't work with Boot properties.
One alternative would be to set those values as a string instead of a hash in the JSON, as in "auth-scopes": "group-1:superadmin,group-2:admin" or "auth-scopes": "group-1=superadmin,group-2=admin". Your SecurityProperties could then get the value of vcap.services.my-service.credentials.auth-scopes and parse the String into a Map.

Related

Is it possible to make POJOs generated with the Avro Maven plugin play nice with Jackson?

The problem I'm having with Avro is that we are required to use title case names for our data model, but camel case for the rest of our data structures such as DTOs. Here is an example:
{
"name": "UserRecord",
"type": "record",
"fields": [
{
"name": "Username",
"type": "string"
}
]
}
#Data
#NoArgsConstructor
#AllArgsConstructor
public class CreateUserRequestDTO {
private UserRecord userRecord;
}
The Maven Avro plugin will generate the Username field like this:
private String Username;
So we have an ugly mix in our code of fields that are title-cased in some cases and camel cased in others. Is there a good way to make Jackson's ObjectMapper able to read both of these property name conventions?
The case mixing is resulting in the Avro fields that are title-cased not getting recognized by Spring Boot's ObjectMapper and all of their fields are null.
I do not know if it will actually work in your use case, but maybe you can enable the Jackson's mapper configuration property ACCEPT_CASE_INSENSITIVE_PROPERTIES:
If enabled, the bean properties will be matched using their lower-case equivalents, meaning that any case-combination (incoming and matching names are canonicalized by lower-casing) should work.
You can customize the value in your application configuration file under the configuration key spring.jackson.mapper.*, something like:
spring:
jackson:
mapper:
ACCEPT_CASE_INSENSITIVE_PROPERTIES: true

Inject values with Spring and YAML with nested properties

I want to inject some values from a YAML to the Spring context.
The structure of the YAML is similar so I did not want to duplicate code, but the Spring startup is failing because it is not being able to inject the value to the placeholder.
Please note my application.properties:
server.port=8084
activeProfile=dev
autoAgents.supplier.id=0
autoAgents.supplier.name=test
autoAgents.supplier.serviceType=REST
autoAgents.supplier.authType=1
autoAgents.supplier.adapter=test
autoAgents.supplier.username=test
autoAgents.supplier.secret=test
autoAgents.supplier.apiPassword=12345
autoAgents.client.id=1
autoAgents.client.name=test
autoAgents.client.serviceType=REST
autoAgents.client.authType=1
autoAgents.client.adapter=
autoAgents.client.username=test
autoAgents.client.secret=test
autoAgents.client.apiPassword=12345
Then I am injecting this values on the YAML, application.yml
activeProfile: ${activeProfile}
autoAgents:
supplier:
isSupplier: true
meta:
id: ${autoAgents.supplier.id}
name: ${autoAgents.supplier.name}
serviceType: ${autoAgents.supplier.serviceType}
authType: ${autoAgents.supplier.authType}
adapter: ${autoAgents.supplier.adapter}
credentials:
username: ${autoAgents.supplier.username}
secret: ${autoAgents.supplier.secret}
apiPassword: ${autoAgents.supplier.apiPassword}
client:
isSupplier: false
meta:
id: ${autoAgents.client.id}
name: ${autoAgents.client.name}
serviceType: ${autoAgents.client.serviceType}
authType: ${autoAgents.client.authType}
adapter: ${autoAgents.client.adapter}
credentials:
username: ${autoAgents.client.username}
secret: ${autoAgents.client.secret}
apiPassword: ${autoAgents.client.apiPassword}
And then I am importing this to a configuration property context:
#Configuration
#EnableConfigurationProperties
#ConfigurationProperties
#Data
public class TwoConnectConfigurationProperties {
private String activeProfile;
#Value("${autoAgents.supplier}")
private AutoAgentDup supplier;
#Value("${autoAgents.client}")
private AutoAgentDup client;
}
But #Value("${autoAgents.supplier}") is not working.
Please advise.
As mentioned earlier it does not make sense to inject values to yaml, you can just create the "application.yaml" with the values directly. And just delete the ".properies" file.
You might want to take a look how to easily inject the properties with common suffix into a bean. Its nicely described here:
https://www.baeldung.com/configuration-properties-in-spring-boot
You will have a bean:
#Configuration
#ConfigurationProperties(prefix = "autoAgents.supplier")
public class AutoAgentSupplierProperties {
private long id;
private String name;
// ... rest of the properies properties
}
You might want the same for the "auto.agent" client.
If you want to avoid code duplication, you can have a bean with the common properties. Extend that class with 2 new classes. One for supplier and one for agent - and annotate those with
#ConfigurationProperties
annotation.
Why you need "nested properties"? If you only want to access them in application, just take values from .properties file and fill them as values to .yml file. E.g.: profile: dev, or
autoAgents:
client:
id: 1
Properties from .yml file can be accessed from code same way as from .properties file.
Your problem is in way how you access properties. When you use "#Configuration properties", you have to specific which one to use (e.g. #ConfigurationProperties("autoAgents.client").

Using Swagger-annotation to represent a complex property as a String

In a Java/Spring ReST application, I'm using swagger-annotations 1.3.7 I have a number of small classes (for example, GenderCode) that I use as properties in my ReST models. These classes have a single public property, called value. Using Jackson, my APIs can accept a simple String s and construct an instance of, say, GenderCode with its value set to s. Similarly, it can serialize a GenderCode as a simple String (which of course represents the value of value).
I would like my Swagger documentation to represent these objects as simple strings, since that represents what the JSON will look like. Instead it represents an complex type with a "value" key:
{
"genderCode": {
"value": ""
},
...
}
It should look simply like this:
{
"genderCode": "",
...
}
Here's what the Java model would look like:
public class Person {
#JsonProperty("genderCode")
#Valid
#KnownEnumValue
#ApiModelProperty(value = "GenderCode", dataType="string", required = false,
allowableValues=GenderCode.POSSIBLE_VALUES_DISPLAY)
private GenderCode genderCode;
...
}
Here's the definition of that property within the API definition file that Swagger generates:
"genderCode":{"enum":["ANY","M","F"],"description":"GenderCode","required":false,"type":"GenderCode"}
I've tried using an OverrideConverter, but that had no effect. Any thoughts on how this can be done?

How to load nested key value pairs from a properties file in SpringBoot

Is there a better manner to implement properties file having key-value pairs as value using Spring/Spring Boot?
I want to create a property file where the key contains a couple of key-value pair as value.
I tried below implementation:-
Properties file:-
Fiat=model:pet,year:1996
Honda=model:dis,year:2000
And i have below class trying to read the properties file.
#Component
#PropertySources(#PropertySource("classpath:sample.properties"))
public class PropertiesExtractor {
#Autowired
private Environment env;
public String pullValue(String node) {
String value = env.getProperty(node);
System.out.println(value);//for Fiat, i get syso as **model:pet,year:1996**
}
}
I need to parse the values using java, to get the individual value. Is this the only way out to implement this.
Is there a better way to use nested property files in Java?
Create a Car object or something with a model and a year property. Then create something like this
#ConfigurationProperties("foo")
public class CarProperties {
private Map<String,Car> cars;
// Getters/Setters
}
Add add #EnableConfigurationProperties(CarProperties.class) in your main configuration class.
Then you can inject that config as follows:
foo.cars.Fiat.model=pet
foo.cars.Fiat.year=1996
foo.cars.Honda.model=dis
foo.cars.Honda.year=2000
There is more info in the doc.
You can use yaml files with spring as well:
http://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html#boot-features-external-config-yaml
This way, you can work with
Fiat:
model: pet
year: 1996
Honda:
model: dis
year: 2000

Mapping Uknown/New MongoDB fields to Map/List - Java

I have a Mongo database and am building a REST API using the Play Framework in Java. I would like to map my Mongo query results to POJOs using some kind of mapper like MongoJack or PlayJongo or something similar.
My problem is that I don't want to have to specify all/new fields in my POJO. What I would like to do is to put all un-mapped fields into a catch all Map as part of the POJO. That way new fields that are added can still be returned in the API response without the Java application having to be aware of them.
Here's an example:
Mongo Document:
{
_id: 1234,
mappedString: "Foo",
mappedInt: 544,
unMappedString: "Test",
unMappedInt: 12
}
Java Class
public class Bar {
private int _id;
private String mappedString;
private int mappedInt;
private Map<String, Object> unMappedFields;
}
I would then expect the unMappedFields field to have two entries in it being "unMappedString" and "unMappedInt" with values "Test" and 12 respectively.
Is there a way to achieve this with one of the Mongo POJO mappers? I've not been able to find any information on if this is possible or not.
Why not to store in mongo as:
{
_id: 1234,
mappedString: "Foo",
mappedInt: 544,
unMappedFields:
{
unMappedString: "Test",
unMappedInt: 12
}
}
I have worked with morphia and this case would be directly parsed.

Categories

Resources